diff --git a/config/alfresco/ehcache-default.xml b/config/alfresco/ehcache-default.xml
index b26824bc19..8d8bbb4f38 100644
--- a/config/alfresco/ehcache-default.xml
+++ b/config/alfresco/ehcache-default.xml
@@ -11,6 +11,7 @@
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
+ statistics="false"
>
@@ -20,6 +21,7 @@
maxElementsInMemory="50"
eternal="true"
overflowToDisk="false"
+ statistics="false"
/>
@@ -77,6 +86,7 @@
maxElementsInMemory="1000"
eternal="true"
overflowToDisk="false"
+ statistics="false"
/>
@@ -179,6 +203,7 @@
maxElementsInMemory="5000"
eternal="true"
overflowToDisk="false"
+ statistics="false"
/>
@@ -216,102 +245,119 @@
eternal="false"
timeToLiveSeconds="300"
overflowToDisk="false"
+ statistics="false"
/>
@@ -321,6 +367,7 @@
maxElementsInMemory="100"
eternal="true"
overflowToDisk="false"
+ statistics="false"
/>
@@ -330,6 +377,7 @@
maxElementsInMemory="100"
eternal="true"
overflowToDisk="false"
+ statistics="false"
/>
@@ -354,6 +404,7 @@
maxElementsInMemory="10000"
eternal="true"
overflowToDisk="false"
+ statistics="false"
/>
@@ -362,6 +413,7 @@
maxElementsInMemory="10000"
eternal="true"
overflowToDisk="false"
+ statistics="false"
/>
@@ -371,6 +423,7 @@
maxElementsInMemory="100"
eternal="true"
overflowToDisk="false"
+ statistics="false"
/>
@@ -380,6 +433,7 @@
maxElementsInMemory="10000"
eternal="true"
overflowToDisk="false"
+ statistics="false"
/>
@@ -389,6 +443,7 @@
maxElementsInMemory="100"
eternal="true"
overflowToDisk="false"
+ statistics="false"
/>
@@ -398,6 +453,7 @@
maxElementsInMemory="10000"
eternal="true"
overflowToDisk="false"
+ statistics="false"
/>
\ No newline at end of file
diff --git a/config/alfresco/extension/ehcache-custom.xml.sample.cluster b/config/alfresco/extension/ehcache-custom.xml.sample.cluster
index 47c01ddba4..61ef2eeb9d 100644
--- a/config/alfresco/extension/ehcache-custom.xml.sample.cluster
+++ b/config/alfresco/extension/ehcache-custom.xml.sample.cluster
@@ -22,7 +22,9 @@
eternal="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
- overflowToDisk="false">
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="true"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
+ overflowToDisk="false"
+ statistics="false"
+ >
standaloneCache;
@@ -77,9 +77,9 @@ public class CacheTest extends TestCase
public void testSetUp() throws Exception
{
- CacheManager cacheManager = (CacheManager) ctx.getBean("ehCacheManager");
+ CacheManager cacheManager = (CacheManager) ctx.getBean("testEHCacheManager");
assertNotNull(cacheManager);
- CacheManager cacheManagerCheck = (CacheManager) ctx.getBean("ehCacheManager");
+ CacheManager cacheManagerCheck = (CacheManager) ctx.getBean("testEHCacheManager");
assertTrue(cacheManager == cacheManagerCheck);
assertNotNull(serviceRegistry);
@@ -344,7 +344,7 @@ public class CacheTest extends TestCase
*/
public void testPerformance() throws Exception
{
- for (int i = 0; i < 5; i++)
+ for (int i = 0; i < 6; i++)
{
int count = (int) Math.pow(10D, (double)i);
@@ -371,6 +371,32 @@ public class CacheTest extends TestCase
}
}
+ /**
+ * @see #testPerformance()
+ */
+ public static void main(String ... args)
+ {
+ try
+ {
+ CacheTest test = new CacheTest();
+ test.setUp();
+ System.out.println("Press any key to run test ...");
+ System.in.read();
+ test.testPerformance();
+ System.out.println("Press any key to shutdown ...");
+ System.in.read();
+ test.tearDown();
+ }
+ catch (Throwable e)
+ {
+ e.printStackTrace();
+ }
+ finally
+ {
+ ApplicationContextHelper.closeApplicationContext();
+ }
+ }
+
/**
* Starts off with a null in the backing cache and adds a value to the
* transactional cache. There should be no problem with this.
@@ -412,7 +438,7 @@ public class CacheTest extends TestCase
assertEquals("The start value isn't correct", startValue, transactionalCache.get(startKey));
- for (int i = 0; i < 50000; i++)
+ for (int i = 0; i < 205000; i++)
{
Object value = Integer.valueOf(i);
String key = value.toString();
diff --git a/source/java/org/alfresco/repo/cache/EhCacheManagerFactoryBean.java b/source/java/org/alfresco/repo/cache/EhCacheManagerFactoryBean.java
index 43009ffe49..df29312b98 100644
--- a/source/java/org/alfresco/repo/cache/EhCacheManagerFactoryBean.java
+++ b/source/java/org/alfresco/repo/cache/EhCacheManagerFactoryBean.java
@@ -43,6 +43,13 @@ import org.springframework.core.io.Resource;
*/
public class EhCacheManagerFactoryBean implements FactoryBean, InitializingBean, DisposableBean
{
+ static
+ {
+ // https://jira.terracotta.org/jira/browse/EHC-652
+ // Force old-style LruMemoryStore
+ System.setProperty("net.sf.ehcache.use.classic.lru", "true");
+ }
+
protected final Log logger = LogFactory.getLog(EhCacheManagerFactoryBean.class);
private Resource configLocation;
diff --git a/source/java/org/alfresco/repo/cache/InternalEhCacheManagerFactoryBean.java b/source/java/org/alfresco/repo/cache/InternalEhCacheManagerFactoryBean.java
index 08ce8b124d..6ccab7c1bb 100644
--- a/source/java/org/alfresco/repo/cache/InternalEhCacheManagerFactoryBean.java
+++ b/source/java/org/alfresco/repo/cache/InternalEhCacheManagerFactoryBean.java
@@ -63,6 +63,13 @@ import org.springframework.util.ResourceUtils;
*/
public class InternalEhCacheManagerFactoryBean implements FactoryBean, CacheProvider
{
+ static
+ {
+ // https://jira.terracotta.org/jira/browse/EHC-652
+ // Force old-style LruMemoryStore
+ System.setProperty("net.sf.ehcache.use.classic.lru", "true");
+ }
+
public static final String CUSTOM_CONFIGURATION_FILE = "classpath:alfresco/extension/ehcache-custom.xml";
public static final String DEFAULT_CONFIGURATION_FILE = "classpath:alfresco/ehcache-default.xml";
diff --git a/source/java/org/alfresco/repo/cache/TransactionalCache.java b/source/java/org/alfresco/repo/cache/TransactionalCache.java
index b76af35b4c..e58a1ba0a7 100644
--- a/source/java/org/alfresco/repo/cache/TransactionalCache.java
+++ b/source/java/org/alfresco/repo/cache/TransactionalCache.java
@@ -21,17 +21,17 @@ package org.alfresco.repo.cache;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
-import java.util.List;
+import java.util.Map;
+import java.util.Set;
-import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
-import net.sf.ehcache.Element;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.TransactionListener;
import org.alfresco.util.EqualsHelper;
+import org.apache.commons.collections.map.LRUMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
@@ -83,9 +83,6 @@ public class TransactionalCache
/** the shared cache that will get updated after commits */
private SimpleCache sharedCache;
- /** the manager to control Ehcache caches */
- private CacheManager cacheManager;
-
/** the maximum number of elements to be contained in the cache */
private int maxCacheSize = 500;
@@ -145,13 +142,10 @@ public class TransactionalCache
}
/**
- * Set the manager to activate and control the cache instances
- *
- * @param cacheManager
+ * No-op
*/
public void setCacheManager(CacheManager cacheManager)
{
- this.cacheManager = cacheManager;
}
/**
@@ -185,7 +179,6 @@ public class TransactionalCache
public void afterPropertiesSet() throws Exception
{
Assert.notNull(name, "name property not set");
- Assert.notNull(cacheManager, "cacheManager property not set");
// generate the resource binding key
resourceKeyTxnData = RESOURCE_KEY_TXN_DATA + "." + name;
// Refine the log category
@@ -201,30 +194,14 @@ public class TransactionalCache
TransactionData data = (TransactionData) AlfrescoTransactionSupport.getResource(resourceKeyTxnData);
if (data == null)
{
- String txnId = AlfrescoTransactionSupport.getTransactionId();
data = new TransactionData();
// create and initialize caches
- data.updatedItemsCache = new Cache(
- name + "_"+ txnId + "_updates",
- maxCacheSize, false, true, 0, 0);
- data.removedItemsCache = new Cache(
- name + "_" + txnId + "_removes",
- maxCacheSize, false, true, 0, 0);
- try
- {
- cacheManager.addCache(data.updatedItemsCache);
- cacheManager.addCache(data.removedItemsCache);
- }
- catch (CacheException e)
- {
- throw new AlfrescoRuntimeException("Failed to add txn caches to manager", e);
- }
- finally
- {
- // ensure that we get the transaction callbacks as we have bound the unique
- // transactional caches to a common manager
- AlfrescoTransactionSupport.bindListener(this);
- }
+ data.updatedItemsCache = new LRUMap(maxCacheSize);
+ data.removedItemsCache = new HashSet(maxCacheSize * 2);
+
+ // ensure that we get the transaction callbacks as we have bound the unique
+ // transactional caches to a common manager
+ AlfrescoTransactionSupport.bindListener(this);
AlfrescoTransactionSupport.bindResource(resourceKeyTxnData, data);
}
return data;
@@ -266,9 +243,9 @@ public class TransactionalCache
keys.addAll(backingKeys);
}
// add keys
- keys.addAll((Collection) txnData.updatedItemsCache.getKeys());
+ keys.addAll(txnData.updatedItemsCache.keySet());
// remove keys
- keys.removeAll((Collection) txnData.removedItemsCache.getKeys());
+ keys.removeAll(txnData.removedItemsCache);
}
else
{
@@ -324,7 +301,7 @@ public class TransactionalCache
if (!txnData.isClearOn) // deletions cache only useful before a clear
{
// check to see if the key is present in the transaction's removed items
- if (txnData.removedItemsCache.get(key) != null)
+ if (txnData.removedItemsCache.contains(key))
{
// it has been removed in this transaction
if (isDebugEnabled)
@@ -338,10 +315,9 @@ public class TransactionalCache
}
// check for the item in the transaction's new/updated items
- Element element = txnData.updatedItemsCache.get(key);
- if (element != null)
+ CacheBucket bucket = (CacheBucket) txnData.updatedItemsCache.get(key);
+ if (bucket != null)
{
- CacheBucket bucket = (CacheBucket) element.getValue();
V value = bucket.getValue();
// element was found in transaction-specific updates/additions
if (isDebugEnabled)
@@ -429,7 +405,7 @@ public class TransactionalCache
{
// we have an active transaction - add the item into the updated cache for this transaction
// are we in an overflow condition?
- if (txnData.updatedItemsCache.getMemoryStoreSize() >= maxCacheSize)
+ if (txnData.updatedItemsCache.isFull())
{
// overflow about to occur or has occured - we can only guarantee non-stale
// data by clearing the shared cache after the transaction. Also, the
@@ -456,8 +432,7 @@ public class TransactionalCache
// The value didn't exist before
bucket = new NewCacheBucket(nullMarker, value);
}
- Element element = new Element(key, bucket);
- txnData.updatedItemsCache.put(element);
+ txnData.updatedItemsCache.put(key, bucket);
// remove the item from the removed cache, if present
txnData.removedItemsCache.remove(key);
// done
@@ -517,7 +492,7 @@ public class TransactionalCache
else
{
// are we in an overflow condition?
- if (txnData.removedItemsCache.getMemoryStoreSize() >= maxCacheSize)
+ if (txnData.removedItemsCache.size() >= maxCacheSize)
{
// overflow about to occur or has occured - we can only guarantee non-stale
// data by clearing the shared cache after the transaction. Also, the
@@ -539,9 +514,7 @@ public class TransactionalCache
else
{
// Create a bucket to remove the value from the shared cache
- CacheBucket removeBucket = new RemoveCacheBucket(existingValue);
- Element element = new Element(key, removeBucket);
- txnData.removedItemsCache.put(element);
+ txnData.removedItemsCache.add(key);
}
}
}
@@ -590,8 +563,8 @@ public class TransactionalCache
// and also serves to ensure that the shared cache will be ignored
// for the remainder of the transaction
txnData.isClearOn = true;
- txnData.updatedItemsCache.removeAll();
- txnData.removedItemsCache.removeAll();
+ txnData.updatedItemsCache.clear();
+ txnData.removedItemsCache.clear();
}
}
else // no transaction
@@ -654,23 +627,22 @@ public class TransactionalCache
// transfer any removed items
// any removed items will have also been removed from the in-transaction updates
// propogate the deletes to the shared cache
- List keys = txnData.removedItemsCache.getKeys();
- for (Serializable key : keys)
+ for (Serializable key : txnData.removedItemsCache)
{
sharedCache.remove(key);
}
if (isDebugEnabled)
{
- logger.debug("Removed " + keys.size() + " values from shared cache");
+ logger.debug("Removed " + txnData.removedItemsCache.size() + " values from shared cache");
}
}
// transfer updates
- List keys = txnData.updatedItemsCache.getKeys();
- for (Serializable key : keys)
+ Set keys = (Set) txnData.updatedItemsCache.keySet();
+ for (Map.Entry> entry : (Set>>) txnData.updatedItemsCache.entrySet())
{
- Element element = txnData.updatedItemsCache.get(key);
- CacheBucket bucket = (CacheBucket) element.getObjectValue();
+ K key = entry.getKey();
+ CacheBucket bucket = entry.getValue();
bucket.doPostCommit(sharedCache, key);
}
if (isDebugEnabled)
@@ -705,8 +677,6 @@ public class TransactionalCache
*/
private void removeCaches(TransactionData txnData)
{
- cacheManager.removeCache(txnData.updatedItemsCache.getName());
- cacheManager.removeCache(txnData.removedItemsCache.getName());
txnData.isClosed = true;
}
@@ -830,30 +800,11 @@ public class TransactionalCache
}
}
- /**
- * Data holder to keep track of cache removals. This bucket assumes the previous existence
- * of an entry in the shared cache.
- */
- private static class RemoveCacheBucket extends UpdateCacheBucket
- {
- private static final long serialVersionUID = -7736719065158540252L;
-
- public RemoveCacheBucket(BV originalValue)
- {
- super(originalValue, null);
- }
- public void doPostCommit(SimpleCache sharedCache, Serializable key)
- {
- // We remove the shared entry whether it has moved on or not
- sharedCache.remove(key);
- }
- }
-
/** Data holder to bind data to the transaction */
private class TransactionData
{
- private Cache updatedItemsCache;
- private Cache removedItemsCache;
+ private LRUMap updatedItemsCache;
+ private Set removedItemsCache;
private boolean haveIssuedFullWarning;
private boolean isClearOn;
private boolean isClosed;
diff --git a/source/test-resources/cache-test/cache-test-config.xml b/source/test-resources/cache-test/cache-test-config.xml
new file mode 100644
index 0000000000..a8a1a4192b
--- /dev/null
+++ b/source/test-resources/cache-test/cache-test-config.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/test-resources/cache-test-context.xml b/source/test-resources/cache-test/cache-test-context.xml
similarity index 73%
rename from source/test-resources/cache-test-context.xml
rename to source/test-resources/cache-test/cache-test-context.xml
index 88f12b190b..d9cfdd5c24 100644
--- a/source/test-resources/cache-test-context.xml
+++ b/source/test-resources/cache-test/cache-test-context.xml
@@ -4,15 +4,18 @@
-
+
- classpath:alfresco/ehcache-default.xml
+ classpath:cache-test/cache-test-config.xml
+
+
+
cache1
@@ -23,6 +26,9 @@
+
+
+
backingCache
@@ -34,20 +40,20 @@
-
-
-
transactionalCache
- 20000
+ 200000
+
+
+
objectCache