jakarta-jcs-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Aaron Smuts <asm...@yahoo.com>
Subject Re: JCS HA cluster, is possible?
Date Tue, 11 Aug 2009 18:05:52 GMT

I'll add something under the JCS util package.  

The utility I'm using looks like this:

/**
 * This handles dividing puts and gets.
 * <p>
 * There are two required properties.
 * <p>
 * <ol>
 * <li>.numberOfPartitions</li>
 * <li>.partitionRegionNamePrefix</li>
 * </ol>
 * System properties will override values in the properties file.
 * <p>
 * We use a JCS region name for each partition that looks like this: partitionRegionNamePrefix
+ "_"
 * + patitionNuber. The number is ) indexed based.
 * <p>
 * @author Aaron Smuts
 */
public class PartitionedJCSCacheImpl
    extends AbstractPropertyContainer
    implements Cache
{
    /** the logger. */
    private static final Log log = LogFactory.getLog( PartitionedJCSCacheImpl.class );

    /** The number of partitions. */
    private int numberOfPartitions = 1;

    /**
     * We use a JCS region name for each partition that looks like this: partitionRegionNamePrefix
+
     * "_" + partitionNumber
     */
    private String partitionRegionNamePrefix;

    /** An array of partitions built during inialization. */
    private JCS[] partitions;

    /** Is the class initialized. */
    private boolean initialized = false;

    /** Sets default properties heading and group. */
    public PartitionedJCSCacheImpl()
    {
        setPropertiesHeading( "PartitionedJCSCache" );
        setPropertiesGroup( "webservices" );
    }

    /**
     * Puts the value into the appropriate cache partition.
     * <p>
     * @param key key 
     * @param object object
     * @return true if there were no errors.
     * @throws ConfigurationException on configuration problem
     */
    public boolean put( Serializable key, Serializable object )
        throws ConfigurationException
    {
        if ( key == null || object == null )
        {
            log.warn( "Bad input key [" + key + "].  Cannot put null into the cache." );
            return false;
        }
        ensureInit();

        int partition = getPartitionNumberForKey( key );
        try
        {
            partitions[partition].put( key, object );
            return true;
        }
        catch ( CacheException e )
        {
            log.error( "Problem putting value for key [" + key + "] in cache [" + partitions[partition]
+ "]" );
            return false;
        }
    }

    /**
     * Gets the object for the key from the desired partition.
     * <p>
     * @param key key
     * @return result, null if not found.
     * @throws ConfigurationException on configuration problem
     */
    public Object get( Serializable key )
        throws ConfigurationException
    {
        if ( key == null )
        {
            log.warn( "Bad input key [" + key + "]." );
            return null;
        }
        ensureInit();

        int partition = getPartitionNumberForKey( key );

        return partitions[partition].get( key );
    }
    
    /**
     * Gets the ICacheElement (the wrapped object) for the key from the desired partition.
     * <p>
     * @param key key
     * @return result, null if not found.
     * @throws ConfigurationException on configuration problem
     */
    public ICacheElement getCacheElement( Serializable key )
        throws ConfigurationException
    {
        if ( key == null )
        {
            log.warn( "Bad input key [" + key + "]." );
            return null;
        }
        ensureInit();

        int partition = getPartitionNumberForKey( key );

        return partitions[partition].getCacheElement( key );
    }

    /**
     * This expects a numeric key. If the key cannot be converted into a number, we wil return
0.
     * TODO we could md5 it or get the hashcode.
     * <p>
     * We determine the partition by taking the mod of the number of partions.
     * <p>
     * @param key key
     * @return the partition number.
     */
    protected int getPartitionNumberForKey( Serializable key )
    {
        if ( key == null )
        {
            return 0;
        }

        long keyNum = getNumericValueForKey( key );

        int partition = (int) ( keyNum % getNumberOfPartitions() );

        if ( log.isDebugEnabled() )
        {
            log.debug( "Using partition [" + partition + "] for key [" + key + "]" );
        }

        return partition;
    }

    /**
     * This can be overridden for special purposes.
     * <p>
     * @param key key
     * @return long
     */
    public long getNumericValueForKey( Serializable key )
    {
        String keyString = key.toString();
        long keyNum = -1;
        try
        {
            keyNum = Long.parseLong( keyString );
        }
        catch ( NumberFormatException e )
        {
            // THIS IS UGLY, but I can't think of a better failsafe right now.
            keyNum = key.hashCode();
            log.warn( "Counldn't convert [" + key + "] into a number.  Will use hashcode ["
+ keyNum + "]" );
        }
        return keyNum;
    }

    /**
     * Initialize if we haven't already.
     * <p>
     * @throws ConfigurationException on configuration problem
     */
    protected synchronized void ensureInit()
        throws ConfigurationException
    {
        if ( !initialized )
        {
            initialize();
        }
    }

    /**
     * Use the partion prefix and the number of partions to get JCS regions.
     * <p>
     * @throws ConfigurationException on configuration problem
     */
    protected synchronized void initialize()
        throws ConfigurationException
    {
        ensureProperties();

        JCS[] tempPartitions = new JCS[this.getNumberOfPartitions()];
        for ( int i = 0; i < this.getNumberOfPartitions(); i++ )
        {
            String regionName = this.getPartitionRegionNamePrefix() + "_" + i;
            try
            {
                tempPartitions[i] = JCS.getInstance( regionName );
            }
            catch ( CacheException e )
            {
                log.error( "Problem getting cache for region [" + regionName + "]" );
            }
        }
        partitions = tempPartitions;
        initialized = true;
    }

    /**
     * Loads in the needed configuration settings. System properties are checked first. A
system
     * property will override local property value.
     * <p>
     * Loads the following JCS Cache specific properties:
     * <ul>
     * <li>heading.numberOfPartitions</li>
     * <li>heading.partitionRegionNamePrefix</li>
     * </ul>
     * @throws ConfigurationException on configuration problem
     */
    protected void handleProperties()
        throws ConfigurationException
    {
        // Number of Partitions.
        String numberOfPartitionsPropertyName = this.getPropertiesHeading() + ".numberOfPartitions";
        String numberOfPartitionsPropertyValue = getPropertyForName( numberOfPartitionsPropertyName,
true );
        try
        {
            this.setNumberOfPartitions( Integer.parseInt( numberOfPartitionsPropertyValue
) );
        }
        catch ( NumberFormatException e )
        {
            String message = "Could not convert [" + numberOfPartitionsPropertyValue + "]
into a number for ["
                + numberOfPartitionsPropertyName + "]";
            log.error( message );
            throw new ConfigurationException( message );
        }

        // Partition Name Prefix.
        String prefixPropertyName = this.getPropertiesHeading() + ".partitionRegionNamePrefix";
        String prefix = getPropertyForName( prefixPropertyName, true );
        this.setPartitionRegionNamePrefix( prefix );
    }

    /**
     * Checks the system properties before the properties.
     * <p>
     * @param propertyName name
     * @param required is it required?
     * @return the property value if one is found
     * @throws ConfigurationException thrown if it is required and not found.
     */
    protected String getPropertyForName( String propertyName, boolean required )
        throws ConfigurationException
    {
        String propertyValue = null;
        propertyValue = System.getProperty( propertyName );
        if ( propertyValue != null )
        {
            if ( log.isInfoEnabled() )
            {
                log.info( "Found system property override: Name [" + propertyName + "] Value
[" + propertyValue + "]" );
            }
        }
        else
        {
            propertyValue = this.getProperties().getProperty( propertyName );
            if ( required && propertyValue == null )
            {
                String message = "Could not find required property [" + propertyName + "]
in propertiesGroup ["
                    + this.getPropertiesGroup() + "]";
                log.error( message );
                throw new ConfigurationException( message );
            }
            else
            {
                if ( log.isInfoEnabled() )
                {
                    log.info( "Name [" + propertyName + "] Value [" + propertyValue + "]"
);
                }
            }
        }
        return propertyValue;
    }

    /**
     * @param numberOfPartitions The numberOfPartitions to set.
     */
    protected void setNumberOfPartitions( int numberOfPartitions )
    {
        this.numberOfPartitions = numberOfPartitions;
    }

    /**
     * @return Returns the numberOfPartitions.
     */
    protected int getNumberOfPartitions()
    {
        return numberOfPartitions;
    }

    /**
     * @param partitionRegionNamePrefix The partitionRegionNamePrefix to set.
     */
    protected void setPartitionRegionNamePrefix( String partitionRegionNamePrefix )
    {
        this.partitionRegionNamePrefix = partitionRegionNamePrefix;
    }

    /**
     * @return Returns the partitionRegionNamePrefix.
     */
    protected String getPartitionRegionNamePrefix()
    {
        return partitionRegionNamePrefix;
    }

    /**
     * @param partitions The partitions to set.
     */
    protected void setPartitions( JCS[] partitions )
    {
        this.partitions = partitions;
    }

    /**
     * @return Returns the partitions.
     */
    protected JCS[] getPartitions()
    {
        return partitions;
    }

--- On Tue, 8/11/09, Aaron Smuts <asmuts@yahoo.com> wrote:

> From: Aaron Smuts <asmuts@yahoo.com>
> Subject: Re: JCS HA cluster, is possible?
> To: "JCS Users List" <jcs-users@jakarta.apache.org>, "rcolmegna@tiscali.it" <rcolmegna@tiscali.it>
> Date: Tuesday, August 11, 2009, 10:54 AM
> 
> You can implement this very easily with a bit of client
> code.  In fact, I've done just this elsewhere, or at
> least something very similar. 
> 
> I have an installation that handles millions of items a
> day.  The items average 80k.  I don't have disk
> space to keep them all on any one box.  So, I
> partitioned the data.  I did it this way.
> 
> I took 8 boxes and setup 4 remote cache server
> primary/failover pairs.  (fyi. Each is configured to
> use a jdbc disk cache backed by mysql running locally.)
> 
> Every client is configured with 4 remote cache client
> auxiliaries, one for each pair.
> 
> I configured 4 regions on the clients.  You could have
> far more.      
> 
> I divided the remote clients between the regions. 
> Region 1 uses p/f pair A.  Region 2, B . . .  
> 
> (In another instance I have 180 regions divided between 4
> remote servers.)
> 
> I decide what region to put the data in through a simple
> algorithm:  key mod numerOfPartitions (here 4) = the
> region.
> 
> I named the regions in a pattern like this:
> 
> MyPartitionedData_0
> MyPartitionedData_1
> MyPartitionedData_2
> MyPartitionedData_3
> 
> I take the suffic from the algorithm and put the data in
> the appropriate region.  I made a simple abstraction
> that does just this. 
> 
> I could add more partitions and reconfigure.  It can
> be scaled indefinitely. . .. 
> 
> Perhaps I'll put in in the JCS util package.
> 
> Cheers,
> 
> Aaron
> 
> 
> 
> --- On Tue, 8/11/09, rcolmegna@tiscali.it
> <rcolmegna@tiscali.it>
> wrote:
> 
> > From: rcolmegna@tiscali.it
> <rcolmegna@tiscali.it>
> > Subject: JCS HA cluster, is possible?
> > To: jcs-users@jakarta.apache.org
> > Date: Tuesday, August 11, 2009, 10:40 AM
> > Hi,
> > 
> > I have a question about JCS behavior.
> > 
> > Suppose this scenario:
> > - 
> > four JCS server: S1,S2,S3,S4
> > - for every server a max of 10,000 
> > cacheable objects
> > - a client C1
> > 
> > I need that client C1 writes 40,000 
> > objects to 4 JCS servers.  JCS could automatic
> balance
> > the 
> > objects 
> > insertion between the 4 servers partitioning the
> objects
> > set (via round 
> > robin, for example)?  
> > 
> > I need the maximum performance (availability), 
> > not affordability.  Substantially I need
> > that an object instance "O1" 
> > is allocated _only_ in one server and that the client
> > lookup
> > is 
> > executed in parallel on the four servers.
> > 
> > Is this possible with JCS?
> > 
> > 
> > TIA
> > Roberto Colmegna
> > 
> > 
> > 
> > 
> > Torna a grande richiesta l'offerta estiva di Tiscali
> Photo
> > !! Non rinuniciare ai tuoi ricordi. Stampa le tue foto
> a
> > soli 0,09 euro
> > 
> > 
> > 
> > http://photo.tiscali.it
> > 
> >
> ---------------------------------------------------------------------
> > To unsubscribe, e-mail: jcs-users-unsubscribe@jakarta.apache.org
> > For additional commands, e-mail: jcs-users-help@jakarta.apache.org
> > 
> >
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: jcs-users-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: jcs-users-help@jakarta.apache.org
> 
> 

---------------------------------------------------------------------
To unsubscribe, e-mail: jcs-users-unsubscribe@jakarta.apache.org
For additional commands, e-mail: jcs-users-help@jakarta.apache.org


Mime
View raw message