From 837095a411cf7a999aa74192d7f82c68ef991a52 Mon Sep 17 00:00:00 2001 From: Britt Park Date: Mon, 20 Nov 2006 22:41:46 +0000 Subject: [PATCH] Another lookup cache checkpoint. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@4405 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/avm-services-context.xml | 3 + .../org/alfresco/repo/avm/LookupCache.java | 186 ++++++++++++++++++ .../java/org/alfresco/repo/avm/LookupKey.java | 130 ++++++++++++ 3 files changed, 319 insertions(+) create mode 100644 source/java/org/alfresco/repo/avm/LookupKey.java diff --git a/config/alfresco/avm-services-context.xml b/config/alfresco/avm-services-context.xml index 7fe5c6b781..cbe5dc6368 100644 --- a/config/alfresco/avm-services-context.xml +++ b/config/alfresco/avm-services-context.xml @@ -127,6 +127,9 @@ + + 200 + diff --git a/source/java/org/alfresco/repo/avm/LookupCache.java b/source/java/org/alfresco/repo/avm/LookupCache.java index 9231f84542..538eee1180 100644 --- a/source/java/org/alfresco/repo/avm/LookupCache.java +++ b/source/java/org/alfresco/repo/avm/LookupCache.java @@ -3,6 +3,13 @@ */ package org.alfresco.repo.avm; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + import org.alfresco.repo.avm.util.SimplePath; import org.apache.log4j.Logger; @@ -14,6 +21,34 @@ public class LookupCache { private static Logger fgLogger = Logger.getLogger(LookupCache.class); + /** + * The Map of of keys to lookups. + */ + private Map fCache; + + /** + * The Map of time stamps to keys. + */ + private SortedMap fTimeStamps; + + /** + * The inverse map of keys to timestamps. + */ + private Map fInverseTimeStamps; + + /** + * The timestamp to next issue. + */ + private long fTimeStamp; + + /** + * The maximum number of lines to have in the cache. + */ + private int fMaxSize; + + /** + * Reference to the Node DAO. + */ private AVMNodeDAO fAVMNodeDAO; /** @@ -21,6 +56,11 @@ public class LookupCache */ public LookupCache() { + fCache = new HashMap(); + fTimeStamps = new TreeMap(); + fInverseTimeStamps = new HashMap(); + fTimeStamp = 0L; + fMaxSize = 100; } /** @@ -32,9 +72,33 @@ public class LookupCache fAVMNodeDAO = dao; } + /** + * Set the maximum cache size. + * @param maxSize + */ + public void setMaxSize(int maxSize) + { + fMaxSize = maxSize; + } + + /** + * Lookup a path. Try to fulfill the request from the cache. + * @param store The AVMStore. + * @param version The versions. + * @param path The path we are looking up. + * @param write Whether this is a write lookup. + * @param includeDeleted + * @return + */ public Lookup lookup(AVMStore store, int version, SimplePath path, boolean write, boolean includeDeleted) { + LookupKey key = new LookupKey(version, path, store.getName(), write, includeDeleted); + Lookup found = findInCache(key); + if (found != null) + { + return found; + } // Make up a Lookup to hold the results. if (path.size() == 0) { @@ -61,6 +125,7 @@ public class LookupCache dir = (DirectoryNode)result.getCurrentNode(); if (path.size() == 1 && path.get(0).equals("")) { + updateCache(key, result); return result; } // Now look up each path element in sequence up to one @@ -89,6 +154,127 @@ public class LookupCache return null; } result.add(child, path.get(path.size() - 1), write); + updateCache(key, result); return result; } + + /** + * Try to find a match in the cache. + * @param key The lookup key. + * @return A valid for this session Lookup or null if not found. + */ + private synchronized Lookup findInCache(LookupKey key) + { + return null; + } + + /** + * Add or update an entry in the cache. + * @param key + * @param lookup + */ + private synchronized void updateCache(LookupKey key, Lookup lookup) + { + if (fCache.containsKey(key)) + { + fCache.remove(key); + Long oldTime = fInverseTimeStamps.get(key); + fInverseTimeStamps.remove(key); + fTimeStamps.remove(oldTime); + } + long timeStamp = fTimeStamp++; + fTimeStamps.put(timeStamp, key); + fInverseTimeStamps.put(key, timeStamp); + fCache.put(key, lookup); + if (fCache.size() > fMaxSize) + { + // Get rid of the oldest entry. + Long oldTime = fTimeStamps.firstKey(); + LookupKey old = fTimeStamps.remove(oldTime); + fInverseTimeStamps.remove(old); + fCache.remove(old); + } + } + + /** + * Remove a List of entries. + * @param keys The List of entries. + */ + private void purgeEntries(List keys) + { + for (LookupKey key : keys) + { + fCache.remove(key); + Long time = fInverseTimeStamps.remove(key); + fTimeStamps.remove(time); + } + } + + // Following are the cache invalidation calls. + + /** + * Called when a simple write operation occurs. This + * invalidates all read lookups and all layered lookups. + */ + public synchronized void onWrite(String storeName) + { + List toDelete = new ArrayList(); + for (Map.Entry entry : fCache.entrySet()) + { + if ((entry.getKey().getStoreName().equals(storeName) && + !entry.getKey().isWrite()) || entry.getValue().isLayered()) + { + toDelete.add(entry.getKey()); + } + } + purgeEntries(toDelete); + } + + /** + * Called when a delete has occurred in a store. This invalidates both + * reads and write lookups in that store. + */ + public synchronized void onDelete(String storeName) + { + List toDelete = new ArrayList(); + for (Map.Entry entry : fCache.entrySet()) + { + if (entry.getKey().getStoreName().equals(storeName) || + entry.getValue().isLayered()) + { + toDelete.add(entry.getKey()); + } + } + purgeEntries(toDelete); + } + + /** + * Called when a snapshot occurs in a store. This invalidates write + * lookups. Read lookups stay untouched. + */ + public synchronized void onSnapshot(String storeName) + { + List toDelete = new ArrayList(); + for (Map.Entry entry : fCache.entrySet()) + { + if ((entry.getKey().getStoreName().equals(storeName) && + entry.getKey().isWrite()) || + entry.getValue().isLayered()) + { + toDelete.add(entry.getKey()); + } + } + purgeEntries(toDelete); + } + + /** + * Called when a rollback has occurred. This invalidates the entire + * cache. Heavy handed but quick. + */ + public synchronized void onRollback() + { + fCache.clear(); + fTimeStamps.clear(); + fInverseTimeStamps.clear(); + } } diff --git a/source/java/org/alfresco/repo/avm/LookupKey.java b/source/java/org/alfresco/repo/avm/LookupKey.java new file mode 100644 index 0000000000..0819e052cf --- /dev/null +++ b/source/java/org/alfresco/repo/avm/LookupKey.java @@ -0,0 +1,130 @@ +/** + * + */ +package org.alfresco.repo.avm; + +import org.alfresco.repo.avm.util.SimplePath; + +/** + * This is the key by which Lookup's are retrieved from the cache. + * @author britt + */ +public class LookupKey +{ + /** + * The name of the store. + */ + private String fStoreName; + + /** + * The path being looked up. + */ + private SimplePath fPath; + + /** + * The version being looked up. + */ + private int fVersion; + + /** + * Whether the lookup is a write lookup. + */ + private boolean fWrite; + + /** + * Whether the lookup includes deleted nodes. + */ + private boolean fIncludeDeleted; + + /** + * Create one from whole cloth. + * @param version The version we're looking under. + * @param path The path. + * @param storeName The name of the store. + * @param write Whether this is a write lookup. + * @param includeDeleted Whether this lookup should include deleted items. + */ + public LookupKey(int version, + SimplePath path, + String storeName, + boolean write, + boolean includeDeleted) + { + fVersion = version; + fPath = path; + fStoreName = storeName; + fWrite = write; + fIncludeDeleted = includeDeleted; + } + + /** + * Set the writeness of this key. + */ + public void setWrite(boolean write) + { + fWrite = write; + } + + /** + * Get the store name for this key. + * @return The store name. + */ + public String getStoreName() + { + return fStoreName; + } + + /** + * Is this a write lookup. + * @return Whether this is a write lookup. + */ + public boolean isWrite() + { + return fWrite; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (!(obj instanceof LookupKey)) + { + return false; + } + LookupKey o = (LookupKey)obj; + return fStoreName.equals(o.fStoreName) && + fVersion == o.fVersion && + fPath.equals(o.fPath) && + fWrite == o.fWrite && + fIncludeDeleted == o.fIncludeDeleted; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() + { + int hash = fStoreName.hashCode(); + hash += fPath.hashCode(); + hash += fVersion; + hash += fWrite ? 1 : 0; + hash += fIncludeDeleted ? 1 : 0; + return hash; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + return fStoreName + ":" + fPath; + } +}