Search code examples
javacachingehcache

What should i do so that i can bootstrap caches from cluster cache peer?


there are three servers and all of them have deployed ehcache. one server is shutdown for some reasons normal or abnormal, when i restart it, i found that cache become empty and data have lose.when i check the other two servers, their cache also become empty and data lose too. i ask this question on ehcach.org/community/ but no reply. i searched answer in ehcache.org(http://www.ehcache.org/documentation/EhcacheUserGuide-1.6.pdf) for days, but i still dont know why. i just found following words, but it doesn't tell me how to do so i still can't avoid above situation.

When a peer comes up, it will be incoherent with other caches. When the bootstrap completes it will be partially coherent. Bootstrap gets the list of keys from a random peer, and then loads those in batches from random peers. If bootstrap fails then the Cache will not start (not like this right now). However if a distributed cache operation occurs which is then overwritten by bootstrap there is a chance that the cache could be inconsistent.

below is my ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>    
<ehcache updateCheck="false">
    <diskStore path="java.io.tmpdir"/>
    <cacheManagerPeerListenerFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" properties="hostName=192.168.4.245, port=7800, socketTimeoutMillis=120000"/>
    <cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" properties="peerDiscovery=manual, rmiUrls=//192.168.4.250:7800/configInfoCache"/>
    <defaultCache maxElementsInMemory="10000" eternal="false" overflowToDisk="true" timeToIdleSeconds="0" timeToLiveSeconds="0" diskPersistent="false" diskExpiryThreadIntervalSeconds="120"> </defaultCache>
    <cache name="configInfoCache" maxElementsInMemory="10000" eternal="true" overflowToDisk="false" timeToIdleSeconds="0" timeToLiveSeconds="0" memoryStoreEvictionPolicy="LFU">
        <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>
        <bootstrapCacheLoaderFactory class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"/>
    </cache>
</ehcache>

ehcahe version 1.6.2 jdk version 1.6 tomcat 6.0.44

hope somebody please tell me how to do, configures in ehcache.xml or implements some interfaces.I have spent to much time on this question.please!!!help me!!!

ps: it's my first time to ask question on stackoverflow, if there need anything, please tell me.


Solution

  • You basically have 2 issues - firstly, other running caches becoming empty when a non-coherent cache starts up and secondly freshly started non-coherent cache doesn't sync automatically with other running caches. Let me try to address it one by one.

    Other running caches becoming empty when a non-coherent cache starts up

    Ehcache has couple of properties which define how the sync'ing between caches will happen, those properties defines whether cache should replicate to other caches by copying or by invalidation. Refer this. So, basically you need to configure Ehcache to "replicate by copy" and not "replicate by invalidation". Once you have taken care of this, your first issue will be resolved.

    For your handy reference I am putting the values you should use (I am using same configuration in my application):

    ##########  EhCache related properties ##########
    peerDiscoveryMechanism=manual
    replicatePuts=true
    replicatePutsViaCopy=true
    replicateUpdates=true
    replicateUpdatesViaCopy=true
    replicateRemovals=true
    replicateAsynchronously=false
    

    Now, you can set this in number of ways, I can tell you how I have done. I have placed these in a properties file and loading it and using in my application. Basically you need to configure these in Echache's RMICacheReplicatorFactory and use this as your cache's cacheEventListenerFactory. So, I have defined below configuration in my ehcache.xml

    <cache name="itdTestResultsCache" maxElementsInMemory="100000" eternal="true" overflowToDisk="false">
        <cacheEventListenerFactory class="com.cgi.itd.customComponents.serverCache.ItdRMICacheReplicatorFactory" />
    </cache>
    

    And then my custom ItdRMICacheReplicatorFactory class is as below. Please note that it has code to load properties and then use it - String replicatePutsString = ITDPropertyPlaceholderConfigurer.getPropertiesMap().get(ITDApplicationConstants.REPLICATE_PUTS);, I am not giving you that piece of code, either you can hardcode or write it yourself.

    import java.util.Properties;
    
    import net.sf.ehcache.distribution.RMICacheReplicatorFactory;
    import net.sf.ehcache.util.PropertyUtil;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    import com.cgi.itd.customComponents.ITDPropertyPlaceholderConfigurer;
    import com.cgi.itd.utils.ITDApplicationConstants;
    
    /**
     * Custom handler for RMI replicator factory
     * @author himanshu.agrawal
     *
     */
    public class ItdRMICacheReplicatorFactory extends RMICacheReplicatorFactory {
        Log log = LogFactory.getLog(ItdRMICacheReplicatorFactory.class);
    
        public ItdRMICacheReplicatorFactory() {
            log.info("ItdRMICacheReplicatorFactory constructor.");
        }
    
        /**
         * Extracts the value of asynchronousReplicationIntervalMillis. Sets it to 1000ms if
         * either not set or there is a problem parsing the number
         * @param properties
         */
        @Override
        protected int extractReplicationIntervalMilis(Properties properties) {
            int asynchronousReplicationIntervalMillis;
            String asynchronousReplicationIntervalMillisString = ITDPropertyPlaceholderConfigurer.getPropertiesMap().get(ITDApplicationConstants.ASYNCHRONOUS_REPLICATION_INTERVAL_MILLIS);
            if (asynchronousReplicationIntervalMillisString != null) {
                try {
                    int asynchronousReplicationIntervalMillisCandidate =
                            Integer.parseInt(asynchronousReplicationIntervalMillisString);
                    if (asynchronousReplicationIntervalMillisCandidate < ITDApplicationConstants.MINIMUM_REASONABLE_INTERVAL) {
                        log.debug("Trying to set the asynchronousReplicationIntervalMillis to an unreasonable number." +
                                " Using the default instead.");
                        asynchronousReplicationIntervalMillis = DEFAULT_ASYNCHRONOUS_REPLICATION_INTERVAL_MILLIS;
                    } else {
                        asynchronousReplicationIntervalMillis = asynchronousReplicationIntervalMillisCandidate;
                    }
                } catch (NumberFormatException e) {
                    log.warn("Number format exception trying to set asynchronousReplicationIntervalMillis. " +
                            "Using the default instead. String value was: '" + asynchronousReplicationIntervalMillisString + "'");
                    asynchronousReplicationIntervalMillis = DEFAULT_ASYNCHRONOUS_REPLICATION_INTERVAL_MILLIS;
                }
            } else {
                asynchronousReplicationIntervalMillis = DEFAULT_ASYNCHRONOUS_REPLICATION_INTERVAL_MILLIS;
            }
            log.debug("Extracted asynchronousReplicationIntervalMillis = " + asynchronousReplicationIntervalMillis);
            return asynchronousReplicationIntervalMillis;
        }
    
        /**
         * Extracts the value of replicateAsynchronously from the properties
         * @param properties
         */
        @Override
        protected boolean extractReplicateAsynchronously(Properties properties) {
            boolean replicateAsynchronously;
            String replicateAsynchronouslyString = ITDPropertyPlaceholderConfigurer.getPropertiesMap().get(ITDApplicationConstants.REPLICATE_ASYNCHRONOUSLY);
            if (replicateAsynchronouslyString != null) {
                replicateAsynchronously = PropertyUtil.parseBoolean(replicateAsynchronouslyString);
            } else {
                replicateAsynchronously = true;
            }
            log.debug("Extracted replicateAsynchronously = " + replicateAsynchronously);
            return replicateAsynchronously;
        }
    
        /**
         * Extracts the value of replicateRemovals from the properties
         * @param properties
         */
        @Override
        protected boolean extractReplicateRemovals(Properties properties) {
            boolean replicateRemovals;
            String replicateRemovalsString = ITDPropertyPlaceholderConfigurer.getPropertiesMap().get(ITDApplicationConstants.REPLICATE_REMOVALS);
            if (replicateRemovalsString != null) {
                replicateRemovals = PropertyUtil.parseBoolean(replicateRemovalsString);
            } else {
                replicateRemovals = true;
            }
            log.debug("Extracted replicateRemovals = " + replicateRemovals);
            return replicateRemovals;
        }
    
        /**
         * Extracts the value of replicateUpdatesViaCopy from the properties
         * @param properties
         */
        @Override
        protected boolean extractReplicateUpdatesViaCopy(Properties properties) {
            boolean replicateUpdatesViaCopy;
            String replicateUpdatesViaCopyString = ITDPropertyPlaceholderConfigurer.getPropertiesMap().get(ITDApplicationConstants.REPLICATE_UPDATES_VIA_COPY);
            if (replicateUpdatesViaCopyString != null) {
                replicateUpdatesViaCopy = PropertyUtil.parseBoolean(replicateUpdatesViaCopyString);
            } else {
                replicateUpdatesViaCopy = true;
            }
            log.debug("Extracted replicateUpdatesViaCopy = " + replicateUpdatesViaCopy);
            return replicateUpdatesViaCopy;
        }
    
        /**
         * Extracts the value of replicatePutsViaCopy from the properties
         * @param properties
         */
        @Override
        protected boolean extractReplicatePutsViaCopy(Properties properties) {
            boolean replicatePutsViaCopy;
            String replicatePutsViaCopyString = ITDPropertyPlaceholderConfigurer.getPropertiesMap().get(ITDApplicationConstants.REPLICATE_PUTS_VIA_COPY);
            if (replicatePutsViaCopyString != null) {
                replicatePutsViaCopy = PropertyUtil.parseBoolean(replicatePutsViaCopyString);
            } else {
                replicatePutsViaCopy = true;
            }
            log.debug("Extracted replicatePutsViaCopy = " + replicatePutsViaCopy);
            return replicatePutsViaCopy;
        }
    
        /**
         * Extracts the value of replicateUpdates from the properties
         * @param properties
         */
        @Override
        protected boolean extractReplicateUpdates(Properties properties) {
            boolean replicateUpdates;
            String replicateUpdatesString = ITDPropertyPlaceholderConfigurer.getPropertiesMap().get(ITDApplicationConstants.REPLICATE_UPDATES);
            if (replicateUpdatesString != null) {
                replicateUpdates = PropertyUtil.parseBoolean(replicateUpdatesString);
            } else {
                replicateUpdates = true;
            }
            log.debug("Extracted replicateUpdates = " + replicateUpdates);
            return replicateUpdates;
        }
    
        /**
         * Extracts the value of replicatePuts from the properties
         * @param properties
         */
        @Override
        protected boolean extractReplicatePuts(Properties properties) {
            boolean replicatePuts;
            String replicatePutsString = ITDPropertyPlaceholderConfigurer.getPropertiesMap().get(ITDApplicationConstants.REPLICATE_PUTS);
            if (replicatePutsString != null) {
                replicatePuts = PropertyUtil.parseBoolean(replicatePutsString);
            } else {
                replicatePuts = true;
            }
            log.debug("Extracted replicatePuts = " + replicatePuts);
            return replicatePuts;
        }
    
    }
    

    So this is will resolve your first issue that when a non-coherent cache comes up then it deletes all the entries in running cache. Now, coming to second one.

    Freshly started non-coherent cache doesn't sync automatically with other running caches

    Refer this answer which tells you how to do it automatically, else you can also do it manually by having a piece of code to load the newly starting up non-coherent cache with values.

    Hint if you don't know how to do it manually - when you application starts up then load the data (from DB or flat file etc.) and then use cache.put to put it into the cache.