diff --git a/source/java/org/alfresco/repo/cache/CacheTest.java b/source/java/org/alfresco/repo/cache/CacheTest.java index 0b705f5ecc..a07a133616 100644 --- a/source/java/org/alfresco/repo/cache/CacheTest.java +++ b/source/java/org/alfresco/repo/cache/CacheTest.java @@ -391,6 +391,36 @@ public class CacheTest extends TestCase } } + /** + * Time how long it takes to create and complete a whole lot of transactions + */ + public void testInitializationPerformance() throws Exception + { + TransactionService transactionService = serviceRegistry.getTransactionService(); + long start = System.nanoTime(); + int count = 10000; + for (int i = 0; i < count; i++) + { + UserTransaction txn = transactionService.getUserTransaction(); + try + { + txn.begin(); + transactionalCache.contains("A"); + } + finally + { + try { txn.rollback(); } catch (Throwable ee) {} + } + } + long end = System.nanoTime(); + + // report + System.out.println( + "Cache initialization performance test: \n" + + " count: " + count + "\n" + + " transaction: " + (end-start)/((long)count) + " ns\\count"); + } + /** * @see #testPerformance() */ @@ -400,7 +430,10 @@ public class CacheTest extends TestCase { CacheTest test = new CacheTest(); test.setUp(); - System.out.println("Press any key to run test ..."); + System.out.println("Press any key to run initialization test ..."); + System.in.read(); + test.testInitializationPerformance(); + System.out.println("Press any key to run performance test ..."); System.in.read(); test.testPerformance(); System.out.println("Press any key to shutdown ..."); diff --git a/source/java/org/alfresco/repo/cache/TransactionalCache.java b/source/java/org/alfresco/repo/cache/TransactionalCache.java index 1c239b7c5a..c745741404 100644 --- a/source/java/org/alfresco/repo/cache/TransactionalCache.java +++ b/source/java/org/alfresco/repo/cache/TransactionalCache.java @@ -21,6 +21,7 @@ package org.alfresco.repo.cache; import java.io.Serializable; import java.util.Collection; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; @@ -30,7 +31,6 @@ 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; @@ -188,7 +188,7 @@ public class TransactionalCache { data = new TransactionData(); // create and initialize caches - data.updatedItemsCache = new LRUMap(maxCacheSize); + data.updatedItemsCache = new LRULinkedHashMap>(23); data.removedItemsCache = new HashSet(13); // ensure that we get the transaction callbacks as we have bound the unique @@ -273,7 +273,6 @@ public class TransactionalCache * Checks the per-transaction caches for the object before going to the shared cache. * If the thread is not in a transaction, then the shared cache is accessed directly. */ - @SuppressWarnings("unchecked") public V get(K key) { boolean ignoreSharedCache = false; @@ -398,7 +397,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.isFull()) + if (txnData.updatedItemsCache.hasHitSize()) { // overflow about to occur or has occured - we can only guarantee non-stale // data by clearing the shared cache after the transaction. Also, the @@ -600,7 +599,6 @@ public class TransactionalCache /** * Merge the transactional caches into the shared cache */ - @SuppressWarnings("unchecked") public void afterCommit() { if (isDebugEnabled) @@ -831,10 +829,38 @@ public class TransactionalCache /** Data holder to bind data to the transaction */ private class TransactionData { - private LRUMap updatedItemsCache; + private LRULinkedHashMap> updatedItemsCache; private Set removedItemsCache; private boolean haveIssuedFullWarning; private boolean isClearOn; private boolean isClosed; } + + /** + * Simple LRU based on {@link LinkedHashMap} + * + * @author Derek Hulley + * @since 3.4 + */ + private class LRULinkedHashMap extends LinkedHashMap + { + private static final long serialVersionUID = -4874684348174271106L; + + private LRULinkedHashMap(int initialSize) + { + super(initialSize); + } + private boolean hasHitSize() + { + return size() >= maxCacheSize; + } + /** + * Remove the eldest entry if the size has reached the maximum cache size + */ + @Override + protected boolean removeEldestEntry(Map.Entry eldest) + { + return (size() > maxCacheSize); + } + } }