TransactionalCache: Ditch Apache Commons LRUMap in favour of a small LinkedHashMap-derived variant

- LRUMap creates a data array as large as the maximum on initialization
 - Added some profile-related tests for initialization to look for bottlenecks


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@20761 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2010-06-22 23:07:28 +00:00
parent 343de9efc9
commit 7af010cf14
2 changed files with 66 additions and 7 deletions

View File

@@ -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() * @see #testPerformance()
*/ */
@@ -400,7 +430,10 @@ public class CacheTest extends TestCase
{ {
CacheTest test = new CacheTest(); CacheTest test = new CacheTest();
test.setUp(); 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(); System.in.read();
test.testPerformance(); test.testPerformance();
System.out.println("Press any key to shutdown ..."); System.out.println("Press any key to shutdown ...");

View File

@@ -21,6 +21,7 @@ package org.alfresco.repo.cache;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -30,7 +31,6 @@ import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.TransactionListener; import org.alfresco.repo.transaction.TransactionListener;
import org.alfresco.util.EqualsHelper; import org.alfresco.util.EqualsHelper;
import org.apache.commons.collections.map.LRUMap;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
@@ -188,7 +188,7 @@ public class TransactionalCache<K extends Serializable, V extends Object>
{ {
data = new TransactionData(); data = new TransactionData();
// create and initialize caches // create and initialize caches
data.updatedItemsCache = new LRUMap(maxCacheSize); data.updatedItemsCache = new LRULinkedHashMap<K, CacheBucket<V>>(23);
data.removedItemsCache = new HashSet<K>(13); data.removedItemsCache = new HashSet<K>(13);
// ensure that we get the transaction callbacks as we have bound the unique // ensure that we get the transaction callbacks as we have bound the unique
@@ -273,7 +273,6 @@ public class TransactionalCache<K extends Serializable, V extends Object>
* Checks the per-transaction caches for the object before going to the shared cache. * 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. * If the thread is not in a transaction, then the shared cache is accessed directly.
*/ */
@SuppressWarnings("unchecked")
public V get(K key) public V get(K key)
{ {
boolean ignoreSharedCache = false; boolean ignoreSharedCache = false;
@@ -398,7 +397,7 @@ public class TransactionalCache<K extends Serializable, V extends Object>
{ {
// we have an active transaction - add the item into the updated cache for this transaction // we have an active transaction - add the item into the updated cache for this transaction
// are we in an overflow condition? // 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 // overflow about to occur or has occured - we can only guarantee non-stale
// data by clearing the shared cache after the transaction. Also, the // data by clearing the shared cache after the transaction. Also, the
@@ -600,7 +599,6 @@ public class TransactionalCache<K extends Serializable, V extends Object>
/** /**
* Merge the transactional caches into the shared cache * Merge the transactional caches into the shared cache
*/ */
@SuppressWarnings("unchecked")
public void afterCommit() public void afterCommit()
{ {
if (isDebugEnabled) if (isDebugEnabled)
@@ -831,10 +829,38 @@ public class TransactionalCache<K extends Serializable, V extends Object>
/** Data holder to bind data to the transaction */ /** Data holder to bind data to the transaction */
private class TransactionData private class TransactionData
{ {
private LRUMap updatedItemsCache; private LRULinkedHashMap<K, CacheBucket<V>> updatedItemsCache;
private Set<K> removedItemsCache; private Set<K> removedItemsCache;
private boolean haveIssuedFullWarning; private boolean haveIssuedFullWarning;
private boolean isClearOn; private boolean isClearOn;
private boolean isClosed; private boolean isClosed;
} }
/**
* Simple LRU based on {@link LinkedHashMap}
*
* @author Derek Hulley
* @since 3.4
*/
private class LRULinkedHashMap<K1, V1> extends LinkedHashMap<K1, V1>
{
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<K1, V1> eldest)
{
return (size() > maxCacheSize);
}
}
} }