From 05c9e52bfbd006ec75613c2b6d5f4d3860ddcd9a Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Fri, 17 Jul 2009 22:11:06 +0000 Subject: [PATCH] Revamp of cache management. We need our cache adapter to create our caches for us so that if we use multiple PersistenceContexts in the same VM they don't all point to the same cache buckets which causes badness. At some point we'll probably have to make it easier to customize these configurations, but this should work for now. --- .../com/samskivert/depot/EHCacheAdapter.java | 310 ++++++++++-------- 1 file changed, 182 insertions(+), 128 deletions(-) diff --git a/src/java/com/samskivert/depot/EHCacheAdapter.java b/src/java/com/samskivert/depot/EHCacheAdapter.java index b2ca126..2e8900e 100644 --- a/src/java/com/samskivert/depot/EHCacheAdapter.java +++ b/src/java/com/samskivert/depot/EHCacheAdapter.java @@ -23,6 +23,7 @@ package com.samskivert.depot; import java.io.Serializable; import java.util.Collections; import java.util.Map; +import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -30,10 +31,14 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.samskivert.util.Histogram; +import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Ehcache; import net.sf.ehcache.Element; +import net.sf.ehcache.distribution.RMICacheReplicatorFactory; import net.sf.ehcache.event.CacheEventListener; +import net.sf.ehcache.event.RegisteredEventListeners; +import net.sf.ehcache.store.MemoryStoreEvictionPolicy; import static com.samskivert.depot.Log.log; @@ -51,11 +56,6 @@ import static com.samskivert.depot.Log.log; public class EHCacheAdapter implements CacheAdapter { - public static final String EHCACHE_RECORD_CACHE = "depotRecord"; - public static final String EHCACHE_SHORT_KEYSET_CACHE = "depotShortKeyset"; - public static final String EHCACHE_LONG_KEYSET_CACHE = "depotLongKeyset"; - public static final String EHCACHE_RESULT_CACHE = "depotResult"; - public static class EHCachePerformance { public Histogram lookups; @@ -64,6 +64,41 @@ public class EHCacheAdapter public Histogram enumerations; } + public static class EHCacheConfig + { + public String name; + public int maxElementsInMemory; + public int timeToIdleSeconds; + public int timeToLiveSeconds; + + public boolean overflowToDisk = false; + public int maxElementsOnDisk = 0; + public boolean eternal = false; + public MemoryStoreEvictionPolicy msep = MemoryStoreEvictionPolicy.LRU; + + public EHCacheConfig (String name, int maxElementsInMemory, int timeToIdleSeconds, + int timeToLiveSeconds) { + this.name = name; + this.maxElementsInMemory = maxElementsInMemory; + this.timeToIdleSeconds = timeToIdleSeconds; + this.timeToLiveSeconds = timeToLiveSeconds; + } + + public Ehcache createCache (String uid) { + return new Cache(name + "." + uid, maxElementsInMemory, msep, overflowToDisk, null, + eternal, timeToLiveSeconds, timeToIdleSeconds, false, 0, null); + } + } + + public static final EHCacheConfig EHCACHE_RECORD_CONFIG = + new EHCacheConfig("depotRecord", 200000, 300, 600); + public static final EHCacheConfig EHCACHE_SHORT_KEYSET_CONFIG = + new EHCacheConfig("depotShortKeyset", 25000, 10, 10); + public static final EHCacheConfig EHCACHE_LONG_KEYSET_CONFIG = + new EHCacheConfig("depotLongKeyset", 25000, 300, 300); + public static final EHCacheConfig EHCACHE_RESULT_CONFIG = + new EHCacheConfig("depotResult", 5000, 300, 300); + /** * Creates an adapter using the supplied cache manager. Note: this adapter does not shut down * the supplied manager when it is shutdown. The caller is responsible for shutting down the @@ -71,10 +106,11 @@ public class EHCacheAdapter */ public EHCacheAdapter (CacheManager cachemgr) { - bindEHCache(cachemgr, CacheCategory.RECORD, EHCACHE_RECORD_CACHE); - bindEHCache(cachemgr, CacheCategory.SHORT_KEYSET, EHCACHE_SHORT_KEYSET_CACHE); - bindEHCache(cachemgr, CacheCategory.LONG_KEYSET, EHCACHE_LONG_KEYSET_CACHE); - bindEHCache(cachemgr, CacheCategory.RESULT, EHCACHE_RESULT_CACHE); + _cachemgr = cachemgr; + createEHCache(CacheCategory.RECORD, EHCACHE_RECORD_CONFIG); + createEHCache(CacheCategory.SHORT_KEYSET, EHCACHE_SHORT_KEYSET_CONFIG); + createEHCache(CacheCategory.LONG_KEYSET, EHCACHE_LONG_KEYSET_CONFIG); + createEHCache(CacheCategory.RESULT, EHCACHE_RESULT_CONFIG); } // from CacheAdapter @@ -141,9 +177,10 @@ public class EHCacheAdapter log.debug("EHCacheAdapter shutting down", "lookups", _lookups, "stores", _stores, "removes", _removes, "enumerations", _enumerations); - // go through and flush all of the caches we resolved + // go through and remove all of the caches we resolved for (Ehcache cache : _categories.values()) { - cache.flush(); + log.debug("Removing ehcache " + cache.getName()); + _cachemgr.removeCache(cache.getName()); } _categories.clear(); _bins.clear(); @@ -183,27 +220,137 @@ public class EHCacheAdapter }; } - protected Ehcache bindEHCache (CacheManager cachemgr, CacheCategory category, String cacheName) + protected Ehcache createEHCache (CacheCategory category, EHCacheConfig config) { - Ehcache cache = cachemgr.getCache(cacheName); - if (cache == null) { - throw new IllegalStateException( - "Could not find Ehcache '" + cacheName + "'. Please fix your ehcache configuration."); - } - cache.getCacheEventNotificationService().registerListener(_cacheEventListener); + Ehcache cache = config.createCache(String.valueOf(hashCode())); + addCacheListeners(cache); + _cachemgr.addCache(cache); + log.debug("Added new ehcache " + cache.getName()); _categories.put(category, cache); return cache; } - protected Histogram _lookups = new Histogram(0, 50, 20); - protected Histogram _stores = new Histogram(0, 50, 20); - protected Histogram _removes = new Histogram(0, 50, 20); - protected Histogram _enumerations = new Histogram(0, 50, 20); + protected void addCacheListeners (Ehcache cache) + { + // add a listener that updates our local cache + cache.getCacheEventNotificationService().registerListener(_cacheEventListener); - protected Map _categories = - Collections.synchronizedMap(Maps.newHashMap()); - protected Map> _bins = - Collections.synchronizedMap(Maps.> newHashMap()); + // add an RMI replicator (TODO: this should be optional) (TODO: do we want only one of + // these and to register the same one with all caches?) + Properties props = new Properties(); + props.setProperty("replicateAsynchronously", "true"); + props.setProperty("replicatePuts", "false"); + props.setProperty("replicateUpdates", "true"); + props.setProperty("replicateUpdatesViaCopy", "false"); + props.setProperty("replicateRemovals", "true"); + cache.getCacheEventNotificationService().registerListener( + new RMICacheReplicatorFactory().createCacheEventListener(props)); + } + + protected static class EHCacheBin + { + public EHCacheBin (Ehcache cache, String id) + { + _cache = cache; + _id = id; + } + + public Set getKeys () + { + return _keys; + } + + public void addKey (Serializable key) + { + _keys.add(key); + } + + public void removeKey (Serializable key) + { + _keys.remove(key); + } + + protected Ehcache getCache () + { + return _cache; + } + + protected Ehcache _cache; + protected String _id; + + protected Set _keys = + Sets.newSetFromMap(new ConcurrentHashMap()); + } + + /** A class to wrap a Depot id/key into an EHCache key. */ + protected static class EHCacheKey + implements Serializable + { + public EHCacheKey (String id, Serializable key) + { + if (id == null || key == null) { + throw new IllegalArgumentException("Can't handle null key or id"); + } + _id = id; + _key = key; + } + + public String getCacheId () { + return _id; + } + + public Serializable getCacheKey () + { + return _key; + } + + @Override + public String toString () + { + return "[" + _id + ", " + _key + "]"; + } + + @Override + public int hashCode () + { + return 31 * _id.hashCode() + _key.hashCode(); + } + + @Override + public boolean equals (Object obj) + { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + EHCacheKey okey = (EHCacheKey) obj; + return _id.equals(okey._id) && _key.equals(okey._key); + } + + protected String _id; + protected Serializable _key; + } + + /** A class to represent an explicitly Serializable concept of null for EHCache. */ + protected static class NullValue implements Serializable + { + @Override public String toString () + { + return ""; + } + + @Override public boolean equals (Object other) + { + return other != null && other.getClass().equals(NullValue.class); + } + + @Override public int hashCode () + { + return 1; + } + } protected CacheEventListener _cacheEventListener = new CacheEventListener() { public Object clone () throws CloneNotSupportedException { @@ -254,110 +401,17 @@ public class EHCacheAdapter } }; - protected static class EHCacheBin - { - public EHCacheBin (Ehcache cache, String id) - { - _cache = cache; - _id = id; - } + protected CacheManager _cachemgr; + protected Histogram _lookups = new Histogram(0, 50, 20); + protected Histogram _stores = new Histogram(0, 50, 20); + protected Histogram _removes = new Histogram(0, 50, 20); + protected Histogram _enumerations = new Histogram(0, 50, 20); - public Set getKeys () - { - return _keys; - } - - public void addKey (Serializable key) - { - _keys.add(key); - } - - public void removeKey (Serializable key) - { - _keys.remove(key); - } - - protected Ehcache getCache () - { - return _cache; - } - - protected Ehcache _cache; - protected String _id; - - protected Set _keys = - Sets.newSetFromMap(new ConcurrentHashMap()); - } + protected Map _categories = + Collections.synchronizedMap(Maps.newHashMap()); + protected Map> _bins = + Collections.synchronizedMap(Maps.> newHashMap()); // this is just for convenience and memory use; we don't rely on pointer equality anywhere protected static Serializable NULL = new NullValue() {}; - - /** A class to wrap a Depot id/key into an EHCache key. */ - protected static class EHCacheKey - implements Serializable - { - public EHCacheKey (String id, Serializable key) - { - if (id == null || key == null) { - throw new IllegalArgumentException("Can't handle null key or id"); - } - _id = id; - _key = key; - } - - public String getCacheId () { - return _id; - } - - public Serializable getCacheKey () - { - return _key; - } - - @Override - public String toString () - { - return "[" + _id + ", " + _key + "]"; - } - - @Override - public int hashCode () - { - return 31 * _id.hashCode() + _key.hashCode(); - } - - @Override - public boolean equals (Object obj) - { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - return _id.equals(((EHCacheKey) obj)._id) && _key.equals(((EHCacheKey) obj)._key); - } - - protected String _id; - protected Serializable _key; - } - - /** A class to represent an explicitly Serializable concept of null for EHCache. */ - protected static class NullValue implements Serializable - { - @Override public String toString () - { - return ""; - } - - @Override public boolean equals (Object other) - { - return other != null && other.getClass().equals(NullValue.class); - } - - @Override public int hashCode () - { - return 1; - } - } }