You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2010/10/02 13:02:07 UTC
svn commit: r1003773 - in /jackrabbit/trunk/jackrabbit-core/src:
main/java/org/apache/jackrabbit/core/cache/
main/java/org/apache/jackrabbit/core/persistence/bundle/
main/java/org/apache/jackrabbit/core/persistence/pool/
main/java/org/apache/jackrabbit...
Author: jukka
Date: Sat Oct 2 11:02:06 2010
New Revision: 1003773
URL: http://svn.apache.org/viewvc?rev=1003773&view=rev
Log:
JCR-2699: Improve read/write concurrency
Revert revision 1003542 until I have time to solve the NPE issue.
Added:
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleCache.java
- copied unchanged from r1003541, jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleCache.java
Removed:
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/cache/LRUCache.java
jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/cache/
Modified:
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/AbstractBundlePersistenceManager.java
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ItemStateCache.java
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ItemStateReferenceCache.java
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/MLRUItemStateCache.java
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ManagedMLRUItemStateCacheFactory.java
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java?rev=1003773&r1=1003772&r2=1003773&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/AbstractBundlePersistenceManager.java Sat Oct 2 11:02:06 2010
@@ -18,7 +18,6 @@ package org.apache.jackrabbit.core.persi
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.apache.jackrabbit.core.cache.LRUCache;
import org.apache.jackrabbit.core.fs.FileSystemResource;
import org.apache.jackrabbit.core.fs.FileSystem;
import org.apache.jackrabbit.core.state.ItemState;
@@ -38,6 +37,7 @@ import org.apache.jackrabbit.core.persis
import org.apache.jackrabbit.core.persistence.PersistenceManager;
import org.apache.jackrabbit.core.util.StringIndex;
import org.apache.jackrabbit.core.persistence.util.BLOBStore;
+import org.apache.jackrabbit.core.persistence.util.BundleCache;
import org.apache.jackrabbit.core.persistence.util.FileBasedIndex;
import org.apache.jackrabbit.core.persistence.util.LRUNodeIdCache;
import org.apache.jackrabbit.core.persistence.util.NodePropBundle;
@@ -68,7 +68,7 @@ import javax.jcr.PropertyType;
* included in the bundle but generated when required.
* <p/>
* In order to increase performance, there are 2 caches maintained. One is the
- * bundle cache that caches already loaded bundles. The other is the
+ * {@link BundleCache} that caches already loaded bundles. The other is the
* {@link LRUNodeIdCache} that caches non-existent bundles. This is useful
* because a lot of {@link #exists(NodeId)} calls are issued that would result
* in a useless SQL execution if the desired bundle does not exist.
@@ -103,7 +103,7 @@ public abstract class AbstractBundlePers
private StringIndex nameIndex;
/** the cache of loaded bundles */
- private LRUCache<NodeId, NodePropBundle> bundles;
+ private BundleCache bundles;
/** the cache of non-existent bundles */
private LRUNodeIdCache missing;
@@ -387,7 +387,7 @@ public abstract class AbstractBundlePers
public void init(PMContext context) throws Exception {
this.context = context;
// init bundle cache
- bundles = new LRUCache<NodeId, NodePropBundle>(bundleCacheSize);
+ bundles = new BundleCache(bundleCacheSize);
missing = new LRUNodeIdCache();
}
@@ -656,7 +656,7 @@ public abstract class AbstractBundlePers
bundle = loadBundle(id);
if (bundle != null) {
bundle.markOld();
- bundles.put(id, bundle, bundle.getSize());
+ bundles.put(bundle);
} else {
missing.put(id);
}
@@ -692,9 +692,8 @@ public abstract class AbstractBundlePers
missing.remove(bundle.getId());
// only put to cache if already exists. this is to ensure proper overwrite
// and not creating big contention during bulk loads
- if (bundles.containsKey(bundle.getId())) {
- bundles.remove(bundle.getId());
- bundles.put(bundle.getId(), bundle, bundle.getSize());
+ if (bundles.contains(bundle.getId())) {
+ bundles.put(bundle);
}
}
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/AbstractBundlePersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/AbstractBundlePersistenceManager.java?rev=1003773&r1=1003772&r2=1003773&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/AbstractBundlePersistenceManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/AbstractBundlePersistenceManager.java Sat Oct 2 11:02:06 2010
@@ -24,7 +24,6 @@ import javax.jcr.PropertyType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.apache.jackrabbit.core.cache.LRUCache;
import org.apache.jackrabbit.core.fs.FileSystemResource;
import org.apache.jackrabbit.core.fs.FileSystem;
import org.apache.jackrabbit.core.id.ItemId;
@@ -35,6 +34,7 @@ import org.apache.jackrabbit.core.persis
import org.apache.jackrabbit.core.persistence.PMContext;
import org.apache.jackrabbit.core.persistence.PersistenceManager;
import org.apache.jackrabbit.core.persistence.util.BLOBStore;
+import org.apache.jackrabbit.core.persistence.util.BundleCache;
import org.apache.jackrabbit.core.persistence.util.FileBasedIndex;
import org.apache.jackrabbit.core.persistence.util.LRUNodeIdCache;
import org.apache.jackrabbit.core.persistence.util.NodePropBundle;
@@ -68,7 +68,7 @@ import org.apache.jackrabbit.spi.commons
* included in the bundle but generated when required.
* <p/>
* In order to increase performance, there are 2 caches maintained. One is the
- * bundle cache that caches already loaded bundles. The other is the
+ * {@link BundleCache} that caches already loaded bundles. The other is the
* {@link LRUNodeIdCache} that caches non-existent bundles. This is useful
* because a lot of {@link #exists(NodeId)} calls are issued that would result
* in a useless SQL execution if the desired bundle does not exist.
@@ -103,7 +103,7 @@ public abstract class AbstractBundlePers
private StringIndex nameIndex;
/** the cache of loaded bundles */
- private LRUCache<NodeId, NodePropBundle> bundles;
+ private BundleCache bundles;
/** the cache of non-existent bundles */
private LRUNodeIdCache missing;
@@ -387,7 +387,7 @@ public abstract class AbstractBundlePers
public void init(PMContext context) throws Exception {
this.context = context;
// init bundle cache
- bundles = new LRUCache<NodeId, NodePropBundle>(bundleCacheSize);
+ bundles = new BundleCache(bundleCacheSize);
missing = new LRUNodeIdCache();
}
@@ -654,7 +654,7 @@ public abstract class AbstractBundlePers
bundle = loadBundle(id);
if (bundle != null) {
bundle.markOld();
- bundles.put(id, bundle, bundle.getSize());
+ bundles.put(bundle);
} else {
missing.put(id);
}
@@ -690,9 +690,8 @@ public abstract class AbstractBundlePers
missing.remove(bundle.getId());
// only put to cache if already exists. this is to ensure proper overwrite
// and not creating big contention during bulk loads
- if (bundles.containsKey(bundle.getId())) {
- bundles.remove(bundle.getId());
- bundles.put(bundle.getId(), bundle, bundle.getSize());
+ if (bundles.contains(bundle.getId())) {
+ bundles.put(bundle);
}
}
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ItemStateCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ItemStateCache.java?rev=1003773&r1=1003772&r2=1003773&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ItemStateCache.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ItemStateCache.java Sat Oct 2 11:02:06 2010
@@ -83,6 +83,14 @@ public interface ItemStateCache {
boolean isEmpty();
/**
+ * Informs the cache that the item was modified and the cache might need to
+ * recalculate the items caching weight.
+ *
+ * @param id the id of the item that was modified.
+ */
+ void update(ItemId id);
+
+ /**
* Informs the cache that it is no longer in use.
*/
void dispose();
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ItemStateReferenceCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ItemStateReferenceCache.java?rev=1003773&r1=1003772&r2=1003773&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ItemStateReferenceCache.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ItemStateReferenceCache.java Sat Oct 2 11:02:06 2010
@@ -162,6 +162,14 @@ public class ItemStateReferenceCache imp
/**
* {@inheritDoc}
*/
+ public synchronized void update(ItemId id) {
+ // delegate
+ cache.update(id);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public synchronized boolean isEmpty() {
// check primary cache
return refs.isEmpty();
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/MLRUItemStateCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/MLRUItemStateCache.java?rev=1003773&r1=1003772&r2=1003773&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/MLRUItemStateCache.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/MLRUItemStateCache.java Sat Oct 2 11:02:06 2010
@@ -16,9 +16,14 @@
*/
package org.apache.jackrabbit.core.state;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
import org.apache.commons.collections.map.LinkedMap;
-import org.apache.jackrabbit.core.cache.CacheManager;
-import org.apache.jackrabbit.core.cache.LRUCache;
+import org.apache.jackrabbit.core.cache.Cache;
+import org.apache.jackrabbit.core.cache.CacheAccessListener;
import org.apache.jackrabbit.core.id.ItemId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,58 +38,139 @@ import org.slf4j.LoggerFactory;
* TODO rename class to something more appropriate, e.g. FIFOItemSateCache since
* it doesn't use a LRU eviction policy anymore.
*/
-public class MLRUItemStateCache implements ItemStateCache {
-
+public class MLRUItemStateCache implements ItemStateCache, Cache {
/** Logger instance */
private static Logger log = LoggerFactory.getLogger(MLRUItemStateCache.class);
/** default maximum memory to use */
public static final int DEFAULT_MAX_MEM = 4 * 1024 * 1024;
+ /** the amount of memory the entries use */
+ private volatile long totalMem;
+
+ /** the maximum of memory the cache may use */
+ private volatile long maxMem;
+
/** the number of writes */
- private volatile long numWrites = 0;
+ private volatile long numWrites;
+
+ /** the access count */
+ private volatile long accessCount;
- private final LRUCache<ItemId, ItemState> cache =
- new LRUCache<ItemId, ItemState>(DEFAULT_MAX_MEM);
+ /** the cache access listeners */
+ private CacheAccessListener accessListener;
- public MLRUItemStateCache(CacheManager cacheMgr) {
- cacheMgr.add(cache);
- cache.setAccessListener(cacheMgr);
+ /**
+ * A cache for <code>ItemState</code> instances
+ */
+ private final Map<ItemId, Entry> cache;
+
+ /**
+ * Constructs a new, empty <code>ItemStateCache</code> with a maximum amount
+ * of memory of {@link #DEFAULT_MAX_MEM}.
+ */
+ public MLRUItemStateCache() {
+ this(DEFAULT_MAX_MEM);
}
- //-------------------------------------------------------< ItemStateCache >
+ /**
+ * Constructs a new, empty <code>ItemStateCache</code> with the specified
+ * maximum memory.
+ *
+ * @param maxMem the maximum amount of memory this cache may use.
+ */
+ @SuppressWarnings("serial")
+ private MLRUItemStateCache(int maxMem) {
+ this.maxMem = maxMem;
+ this.cache = new LinkedHashMap<ItemId, MLRUItemStateCache.Entry>(
+ maxMem / 1024, 0.75f, true /* access-ordered */) {
+ @Override
+ protected boolean removeEldestEntry(Map.Entry<ItemId, Entry> e) {
+ long maxMem = MLRUItemStateCache.this.maxMem;
+ if (totalMem <= maxMem) {
+ return false;
+ } else if (totalMem - e.getValue().size <= maxMem) {
+ totalMem -= e.getValue().size;
+ return true;
+ } else {
+ shrink();
+ return false;
+ }
+ }
+ };
+ }
+ //-------------------------------------------------------< ItemStateCache >
/**
* {@inheritDoc}
*/
public boolean isCached(ItemId id) {
- return cache.containsKey(id);
+ synchronized (cache) {
+ return cache.containsKey(id);
+ }
}
/**
* {@inheritDoc}
*/
public ItemState retrieve(ItemId id) {
- return cache.get(id);
+ touch();
+ synchronized (cache) {
+ Entry entry = cache.get(id);
+ if (entry != null) {
+ return entry.state;
+ } else {
+ return null;
+ }
+ }
}
/**
* {@inheritDoc}
*/
public ItemState[] retrieveAll() {
- return cache.values();
+ synchronized (cache) {
+ ItemState[] states = new ItemState[cache.size()];
+ int i = 0;
+ for (Entry entry : cache.values()) {
+ states[i++] = entry.state;
+ }
+ return states;
+ }
}
/**
* {@inheritDoc}
*/
- public synchronized void cache(ItemState state) {
- cache.put(state.getId(), state, state.calculateMemoryFootprint());
+ public void update(ItemId id) {
+ touch();
+ synchronized (cache) {
+ Entry entry = cache.get(id);
+ if (entry != null) {
+ totalMem -= entry.size;
+ entry.recalc();
+ totalMem += entry.size;
+ }
+ }
+ }
- if (numWrites++ % 10000 == 0 && log.isDebugEnabled()) {
- log.debug("Item state cache size: {}% of {} bytes",
- cache.getMemoryUsed() * 100 / cache.getMaxMemorySize(),
- cache.getMaxMemorySize());
+ /**
+ * {@inheritDoc}
+ */
+ public void cache(ItemState state) {
+ touch();
+ synchronized (cache) {
+ ItemId id = state.getId();
+ if (cache.containsKey(id)) {
+ log.warn("overwriting cached entry " + id);
+ evict(id);
+ }
+ Entry entry = new Entry(state);
+ totalMem += entry.size;
+ cache.put(id, entry);
+ if (numWrites++ % 10000 == 0 && log.isDebugEnabled()) {
+ log.debug(this + " size=" + cache.size() + ", " + totalMem + "/" + maxMem);
+ }
}
}
@@ -92,28 +178,135 @@ public class MLRUItemStateCache implemen
* {@inheritDoc}
*/
public void evict(ItemId id) {
- cache.remove(id);
+ touch();
+ synchronized (cache) {
+ Entry entry = cache.remove(id);
+ if (entry != null) {
+ totalMem -= entry.size;
+ }
+ }
}
/**
* {@inheritDoc}
*/
public void evictAll() {
- cache.clear();
+ synchronized (cache) {
+ cache.clear();
+ totalMem = 0;
+ }
}
/**
* {@inheritDoc}
*/
public boolean isEmpty() {
- return cache.isEmpty();
+ synchronized (cache) {
+ return cache.isEmpty();
+ }
+ }
+
+ private void touch() {
+ accessCount++;
+ if ((accessCount % CacheAccessListener.ACCESS_INTERVAL) == 0) {
+ if (accessListener != null) {
+ accessListener.cacheAccessed();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getAccessCount() {
+ return accessCount;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getMaxMemorySize() {
+ return maxMem;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getMemoryUsed() {
+ return totalMem;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void resetAccessCount() {
+ synchronized (cache) {
+ accessCount = 0;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setMaxMemorySize(long size) {
+ synchronized (cache) {
+ this.maxMem = size;
+
+ // remove items, if too many
+ if (totalMem > maxMem) {
+ shrink();
+ }
+ }
+ }
+
+ private void shrink() {
+ List<Map.Entry<ItemId, Entry>> list =
+ new ArrayList<Map.Entry<ItemId, Entry>>(cache.entrySet());
+ for (int i = list.size() - 1; totalMem > maxMem && i >= 0; i--) {
+ Map.Entry<ItemId, Entry> last = list.get(i);
+ totalMem -= last.getValue().size;
+ cache.remove(last.getKey());
+ }
+ }
+
+ /**
+ * Set the cache access listener. Only one listener per cache is supported.
+ *
+ * @param listener the new listener
+ */
+ public void setAccessListener(CacheAccessListener listener) {
+ this.accessListener = listener;
}
/**
* {@inheritDoc}
*/
public void dispose() {
- cache.dispose();
+ synchronized (cache) {
+ if (accessListener != null) {
+ accessListener.disposeCache(this);
+ }
+ }
+ }
+
+
+ /**
+ * Internal cache entry.
+ */
+ private static class Entry {
+
+ private final ItemState state;
+
+ private long size;
+
+ public Entry(ItemState state) {
+ this.state = state;
+ this.size = 64 + state.calculateMemoryFootprint();
+ }
+
+ public void recalc() {
+ size = 64 + state.calculateMemoryFootprint();
+ }
}
}
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ManagedMLRUItemStateCacheFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ManagedMLRUItemStateCacheFactory.java?rev=1003773&r1=1003772&r2=1003773&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ManagedMLRUItemStateCacheFactory.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ManagedMLRUItemStateCacheFactory.java Sat Oct 2 11:02:06 2010
@@ -41,7 +41,10 @@ public class ManagedMLRUItemStateCacheFa
* Create a new cache instance and link it to the cache manager.
*/
public ItemStateCache newItemStateCache() {
- return new MLRUItemStateCache(cacheMgr);
+ MLRUItemStateCache cache = new MLRUItemStateCache();
+ cacheMgr.add(cache);
+ cache.setAccessListener(cacheMgr);
+ return cache;
}
}