Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/XAWorkspace.java
===================================================================
--- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/XAWorkspace.java (revision 472064)
+++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/XAWorkspace.java (working copy)
@@ -53,7 +53,7 @@
* {@inheritDoc}
*/
protected LocalItemStateManager createItemStateManager(SharedItemStateManager shared) {
- return new XAItemStateManager(shared, this);
+ return new XAItemStateManager(shared, this, rep.getItemStateCacheFactory());
}
/**
Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/XASessionImpl.java
===================================================================
--- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/XASessionImpl.java (revision 472064)
+++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/XASessionImpl.java (working copy)
@@ -131,7 +131,7 @@
* Create array that contains all resources that paricipate in this
* transactions. Because some resources depend on each other, there is
* also a workspace scoped lock resource inserted, that guards the
- * entire transaction from deadlocks (see JCR-335)
+ * entire transaction from deadlocks (see JCR-335)
*/
txResources = new InternalXAResource[] {
((XAWorkspace) wsp).getXAResourceBegin(),
@@ -158,7 +158,7 @@
throws RepositoryException {
VersionManagerImpl vMgr = (VersionManagerImpl) rep.getVersionManager();
- return new XAVersionManager(vMgr, rep.getNodeTypeRegistry(), this);
+ return new XAVersionManager(vMgr, rep.getNodeTypeRegistry(), this, rep.getItemStateCacheFactory());
}
/**
@@ -373,6 +373,19 @@
}
/**
+ * {@inheritDoc}
+ */
+ public synchronized void logout() {
+ super.logout();
+ // dispose the caches
+ try {
+ ((XAVersionManager)versionMgr).close();
+ } catch(Exception e) {
+ log.warn("error while closing XAVersionManager", e);
+ }
+ }
+
+ /**
* Compare two strings for equality. If both are null, this
* is also considered to be equal.
*/
Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/ItemStateReferenceCache.java
===================================================================
--- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/ItemStateReferenceCache.java (revision 472064)
+++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/ItemStateReferenceCache.java (working copy)
@@ -66,8 +66,8 @@
* LRUItemStateCache instance as internal secondary
* cache.
*/
- public ItemStateReferenceCache() {
- this(new MLRUItemStateCache());
+ public ItemStateReferenceCache(ItemStateCacheFactory cacheFactory) {
+ this(cacheFactory.newItemStateCache());
}
/**
@@ -130,6 +130,13 @@
/**
* {@inheritDoc}
*/
+ public void dispose() {
+ cache.dispose();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public void evictAll() {
// fake call to update stats of secondary cache
cache.evictAll();
Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/Cache.java
===================================================================
--- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/Cache.java (revision 0)
+++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/Cache.java (revision 0)
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state;
+
+/**
+ * A Cache object
+ * A cache must call CacheManager.getInstance().add(this)
+ * to take part in the dynamic memory distribution.
+ */
+public interface Cache {
+
+ /**
+ * Set the new memory limit.
+ * @param size the size in bytes
+ */
+ void setMaxMemorySize(long size);
+
+ /**
+ * Get the current limit.
+ * @return the size in bytes
+ */
+ long getMaxMemorySize();
+
+ /**
+ * Get the amount of used memory.
+ * @return the size in bytes
+ */
+ long getMemoryUsed();
+
+ /**
+ * Get the number of accesses (get or set) until resetAccessCount was called.
+ * @return the count
+ */
+
+ long getAccessCount();
+
+ /**
+ * Reset the access counter.
+ */
+ void resetAccessCount();
+
+ /**
+ * Add a listener to this cache that is informed after a number of accesses.
+ */
+ void setAccessListener(CacheAccessListener listener);
+
+}
Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java
===================================================================
--- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java (revision 472064)
+++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java (working copy)
@@ -70,8 +70,8 @@
* @param factory event state collection factory
*/
public LocalItemStateManager(SharedItemStateManager sharedStateMgr,
- EventStateCollectionFactory factory) {
- cache = new ItemStateReferenceCache();
+ EventStateCollectionFactory factory, ItemStateCacheFactory cacheFactory) {
+ cache = new ItemStateReferenceCache(cacheFactory);
this.sharedStateMgr = sharedStateMgr;
this.factory = factory;
@@ -344,6 +344,7 @@
}
// clear cache
cache.evictAll();
+ cache.dispose();
}
/**
Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/LRUItemStateCache.java
===================================================================
--- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/LRUItemStateCache.java (revision 472064)
+++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/LRUItemStateCache.java (working copy)
@@ -134,4 +134,11 @@
public Collection values() {
return Collections.unmodifiableCollection(cache.values());
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public void dispose() {
+ cache.clear();
+ }
}
Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
===================================================================
--- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java (revision 474304)
+++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java (working copy)
@@ -196,9 +196,10 @@
public SharedItemStateManager(PersistenceManager persistMgr,
NodeId rootNodeId,
NodeTypeRegistry ntReg,
- boolean usesReferences)
+ boolean usesReferences,
+ ItemStateCacheFactory cacheFactory)
throws ItemStateException {
- cache = new ItemStateReferenceCache();
+ cache = new ItemStateReferenceCache(cacheFactory);
this.persistMgr = persistMgr;
this.ntReg = ntReg;
this.usesReferences = usesReferences;
Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/MLRUItemStateCache.java
===================================================================
--- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/MLRUItemStateCache.java (revision 474304)
+++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/MLRUItemStateCache.java (working copy)
@@ -34,7 +34,7 @@
* cache uses a rough estimate of the memory consuption of the cache item
* states for calculating the maximum number of entries.
*/
-public class MLRUItemStateCache implements ItemStateCache {
+public class MLRUItemStateCache implements ItemStateCache, Cache {
/** Logger instance */
private static Logger log = LoggerFactory.getLogger(LRUItemStateCache.class);
@@ -45,11 +45,17 @@
private long totalMem;
/** the maximum of memory the cache may use */
- private final long maxMem;
+ private long maxMem;
/** the number of writes */
private long numWrites = 0;
+ /** the access count */
+ private long accessCount = 0;
+
+ /** the cache access listeners */
+ private CacheAccessListener accessListener;
+
/**
* A cache for ItemState instances
*/
@@ -69,7 +75,7 @@
*
* @param maxMem the maximum amount of memory this cache may use.
*/
- public MLRUItemStateCache(int maxMem) {
+ private MLRUItemStateCache(int maxMem) {
this.maxMem = maxMem;
}
@@ -88,6 +94,7 @@
*/
public ItemState retrieve(ItemId id) {
synchronized (cache) {
+ touch();
Entry entry = (Entry) cache.remove(id);
if (entry != null) {
// 'touch' item, by adding at end of list
@@ -104,6 +111,7 @@
*/
public void update(ItemId id) {
synchronized (cache) {
+ touch();
Entry entry = (Entry) cache.get(id);
if (entry != null) {
totalMem -= entry.size;
@@ -118,6 +126,7 @@
*/
public void cache(ItemState state) {
synchronized (cache) {
+ touch();
ItemId id = state.getId();
if (cache.containsKey(id)) {
log.warn("overwriting cached entry " + id);
@@ -126,22 +135,27 @@
Entry entry = new Entry(state);
cache.put(id, entry);
totalMem += entry.size;
- // remove items, if too many
- while (totalMem > maxMem) {
- id = (ItemId) cache.firstKey();
- evict(id);
- }
+ shrinkIfRequired();
if (numWrites++%10000 == 0 && log.isDebugEnabled()) {
log.info(this + " size=" + cache.size() + ", " + totalMem + "/" + maxMem);
}
}
}
+ private void shrinkIfRequired() {
+ // remove items, if too many
+ while (totalMem > maxMem) {
+ ItemId id = (ItemId) cache.firstKey();
+ evict(id);
+ }
+ }
+
/**
* {@inheritDoc}
*/
public void evict(ItemId id) {
synchronized (cache) {
+ touch();
Entry entry = (Entry) cache.remove(id);
if (entry != null) {
totalMem -= entry.size;
@@ -201,9 +215,88 @@
}
}
+ private void touch() {
+ accessCount++;
+ if ((accessCount % CacheAccessListener.ACCESS_INTERVAL) == 0) {
+ if (accessListener != null) {
+ accessListener.cacheAccessed();
+ }
+ }
+ }
+
/**
- * Internal cache entry
+ * {@inheritDoc}
*/
+ public long getAccessCount() {
+ return accessCount;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getMaxMemorySize() {
+ return maxMem;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getMemoryUsed() {
+ synchronized (cache) {
+ totalMem = 0;
+ Iterator iter = cache.values().iterator();
+ while (iter.hasNext()) {
+ Entry entry = (Entry) iter.next();
+ entry.recalc();
+ totalMem += entry.size;
+ }
+ }
+ return totalMem;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void resetAccessCount() {
+ synchronized (cache) {
+ accessCount = 0;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setMaxMemorySize(long size) {
+ synchronized (cache) {
+ this.maxMem = size;
+ shrinkIfRequired();
+ }
+ }
+
+ /**
+ * 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() {
+ synchronized (cache) {
+ if(accessListener != null) {
+ accessListener.disposeCache(this);
+ }
+ }
+ }
+
+
+ /**
+ * Internal cache entry.
+ */
private static class Entry {
private final ItemState state;
@@ -219,4 +312,5 @@
size = 64 + state.calculateMemoryFootprint();
}
}
+
}
Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/ManagedMLRUItemStateCacheFactory.java
===================================================================
--- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/ManagedMLRUItemStateCacheFactory.java (revision 0)
+++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/ManagedMLRUItemStateCacheFactory.java (revision 0)
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state;
+
+/**
+ * This class constructs new MLRUItemStateCache.
+ * This class adds the new caches to the cache manager,
+ * and links the caches to the cache manager.
+ */
+public class ManagedMLRUItemStateCacheFactory implements ItemStateCacheFactory {
+
+ /** The cache manager. */
+ private CacheManager cacheMgr;
+
+ /**
+ * Construct a new factory using a cache manager.
+ *
+ * @param cacheMgr the cache manager
+ */
+ public ManagedMLRUItemStateCacheFactory(CacheManager cacheMgr) {
+ this.cacheMgr = cacheMgr;
+ }
+
+ /**
+ * Create a new cache instance and link it to the cache manager.
+ */
+ public ItemStateCache newItemStateCache() {
+ MLRUItemStateCache cache = new MLRUItemStateCache();
+ cacheMgr.add(cache);
+ cache.setAccessListener(cacheMgr);
+ return cache;
+ }
+
+}
Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/CacheAccessListener.java
===================================================================
--- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/CacheAccessListener.java (revision 0)
+++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/CacheAccessListener.java (revision 0)
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state;
+
+/**
+ * The cache access listener can be registerd to a class.
+ * From time to time, the method cacheAccess is called.
+ */
+public interface CacheAccessListener {
+
+ /**
+ * The access listener is only called each x accesses.
+ */
+ static final int ACCESS_INTERVAL = 127;
+
+ /**
+ * The cache calls this method after a number of accessed.
+ */
+ void cacheAccessed();
+
+ /**
+ * Called after the cache is no longer used.
+ */
+ void disposeCache(Cache cache);
+
+}
Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/ItemStateCacheFactory.java
===================================================================
--- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/ItemStateCacheFactory.java (revision 0)
+++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/ItemStateCacheFactory.java (revision 0)
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state;
+
+/**
+ * An item state cache factory to construct new item state caches.
+ */
+public interface ItemStateCacheFactory {
+
+ /**
+ * Construct a new item state cache.
+ *
+ * @return the new cache
+ */
+ ItemStateCache newItemStateCache();
+}
Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/XAItemStateManager.java
===================================================================
--- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/XAItemStateManager.java (revision 472064)
+++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/XAItemStateManager.java (working copy)
@@ -86,8 +86,8 @@
* @param factory event state collection factory
*/
public XAItemStateManager(SharedItemStateManager sharedStateMgr,
- EventStateCollectionFactory factory) {
- this(sharedStateMgr, factory, DEFAULT_ATTRIBUTE_NAME);
+ EventStateCollectionFactory factory, ItemStateCacheFactory cacheFactory) {
+ this(sharedStateMgr, factory, DEFAULT_ATTRIBUTE_NAME, cacheFactory);
}
/**
@@ -99,8 +99,9 @@
*/
public XAItemStateManager(SharedItemStateManager sharedStateMgr,
EventStateCollectionFactory factory,
- String attributeName) {
- super(sharedStateMgr, factory);
+ String attributeName,
+ ItemStateCacheFactory cacheFactory) {
+ super(sharedStateMgr, factory, cacheFactory);
this.attributeName = attributeName;
}
Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/ItemStateCache.java
===================================================================
--- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/ItemStateCache.java (revision 472064)
+++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/ItemStateCache.java (working copy)
@@ -108,4 +108,9 @@
* @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();
}
Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/CacheManager.java
===================================================================
--- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/CacheManager.java (revision 0)
+++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/CacheManager.java (revision 0)
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.WeakHashMap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class manages the size of the caches used in Jackrabbit. The combined
+ * size of all caches must be limited to avoid out of memory problems. The
+ * available memory is dynamically distributed across the caches each second.
+ * This class tries to calculates the best cache sizes by comparing the access
+ * counts of each cache, and the used memory. The idea is, the more a cache is
+ * accessed, the more memory it should get, while the cache should not shrink
+ * too quickly. A minimum and maximum size per cache is defined as well. After
+ * distributing the memory in this way, there might be some unused memory (if
+ * one or more caches did not use some of the allocated memory). This unused
+ * memory is distributed evenly across the full caches.
+ *
+ */
+public class CacheManager implements CacheAccessListener {
+
+ /** The logger instance. */
+ private static Logger log = LoggerFactory.getLogger(CacheManager.class);
+
+ /** The amount of memory to distribute accross the caches. */
+ private static final long MAX_MEMORY = 16 * 1024 * 1024;
+
+ /** The minimum size of a cache. */
+ private static final long MIN_MEMORY_PER_CACHE = 128 * 1024;
+
+ /** The maximum memory per cache (unless, there is some unused memory). */
+ private static final long MAX_MEMORY_PER_CACHE = 4 * 1024 * 1024;
+
+ /** The set of caches (weakly referenced). */
+ private WeakHashMap caches = new WeakHashMap();
+
+ /** Rebalance the caches each ... milliseconds at most. */
+ private static final int SLEEP = 1000;
+
+ /** The size of a big object, to detect if a cache is full or not. */
+ private static final int BIG_OBJECT_SIZE = 16 * 1024;
+
+ /** The last time the caches where resized. */
+ private volatile long nextResize = System.currentTimeMillis() + SLEEP;
+
+ /**
+ * After one of the caches is accessed a number of times, this method is called.
+ * Resize the caches if required.
+ */
+ public void cacheAccessed() {
+ long now = System.currentTimeMillis();
+ if (now < nextResize) {
+ return;
+ }
+ synchronized (this) {
+ // the previous test was not synchronized (for speed)
+ // so we need another synchronized test
+ if (now < nextResize) {
+ return;
+ }
+ nextResize = now + SLEEP;
+ resizeAll();
+ nextResize = System.currentTimeMillis() + SLEEP;
+ }
+ }
+
+ /**
+ * Re-calcualte the maximum memory for each cache, and set the new limits.
+ */
+ private void resizeAll() {
+ log.info("resizeAll size="+ caches.size());
+ // get strong references
+ // entries in a weak hash map may disappear any time
+ // so can't use size() / keySet() directly
+ // only using the iterator guarantees that we don't get null references
+ ArrayList list = new ArrayList();
+ for (Iterator it = caches.keySet().iterator(); it.hasNext();) {
+ list.add(it.next());
+ }
+ if (list.size() == 0) {
+ // nothing to do
+ return;
+ }
+ CacheInfo[] infos = new CacheInfo[list.size()];
+ for (int i = 0; i < list.size(); i++) {
+ infos[i] = new CacheInfo((Cache) list.get(i));
+ }
+ // calculate the total access count and memory used
+ long totalAccessCount = 0;
+ long totalMemoryUsed = 0;
+ for (int i = 0; i < infos.length; i++) {
+ totalAccessCount += infos[i].getAccessCount();
+ totalMemoryUsed += infos[i].getMemoryUsed();
+ }
+ // try to distribute the memory based on the access count
+ // and memory used (higher numbers - more memory)
+ // and find out how many caches are full
+ // 50% is distributed according to access count,
+ // and 50% according to memory used
+ double memoryPerAccess = (double) MAX_MEMORY / 2.
+ / Math.max(1., (double) totalAccessCount);
+ double memoryPerUsed = (double) MAX_MEMORY / 2.
+ / Math.max(1., (double) totalMemoryUsed);
+ int fullCacheCount = 0;
+ for (int i = 0; i < infos.length; i++) {
+ CacheInfo info = infos[i];
+ long mem = (long) (memoryPerAccess * info.getAccessCount());
+ mem += (long) (memoryPerUsed * info.getMemoryUsed());
+ mem = Math.min(mem, MAX_MEMORY_PER_CACHE);
+ if (info.wasFull()) {
+ fullCacheCount++;
+ } else {
+ mem = Math.min(mem, info.getMemoryUsed());
+ }
+ mem = Math.min(mem, MAX_MEMORY_PER_CACHE);
+ mem = Math.max(mem, MIN_MEMORY_PER_CACHE);
+ info.setMemory(mem);
+ }
+ // calculate the unused memory
+ long unusedMemory = MAX_MEMORY;
+ for (int i = 0; i < infos.length; i++) {
+ unusedMemory -= infos[i].getMemory();
+ }
+ // distribute the remaining memory evenly across the full caches
+ if (unusedMemory > 0 && fullCacheCount > 0) {
+ for (int i = 0; i < infos.length; i++) {
+ CacheInfo info = infos[i];
+ if (info.wasFull()) {
+ info.setMemory(info.getMemory() + unusedMemory
+ / fullCacheCount);
+ }
+ }
+ }
+ // set the new limit
+ for (int i = 0; i < infos.length; i++) {
+ CacheInfo info = infos[i];
+ Cache cache = info.getCache();
+ log.debug(cache + " now:" + cache.getMaxMemorySize() + " used:"
+ + info.getMemoryUsed() + " access:" + info.getAccessCount()
+ + " new:" + info.getMemory());
+ cache.setMaxMemorySize(info.getMemory());
+ }
+ }
+
+ /**
+ * Add a new cache to the list.
+ * This call does not trigger recalculating the cache sizes.
+ *
+ * @param cache the cache to add
+ */
+ public synchronized void add(Cache cache) {
+ caches.put(cache, null);
+ }
+
+ /**
+ * Remove a cache. As this class only has a weak reference to each cache,
+ * calling this method is not strictly required.
+ * This call does not trigger recalculating the cache sizes.
+ *
+ * @param cache
+ * the cache to remove
+ */
+ public synchronized void remove(Cache cache) {
+ caches.remove(cache);
+ }
+
+ /**
+ * Internal copy of the cache information.
+ */
+ public static class CacheInfo {
+ private Cache cache;
+
+ private long accessCount;
+
+ private long memory;
+
+ private long memoryUsed;
+
+ private boolean wasFull;
+
+ CacheInfo(Cache cache) {
+ this.cache = cache;
+ // copy the data as this runs in a different thread
+ // the exact values are not important, but it is important that the
+ // values don't change
+ this.memory = cache.getMaxMemorySize();
+ this.memoryUsed = cache.getMemoryUsed();
+ this.accessCount = cache.getAccessCount();
+ // reset the access count, so that concurrent cache access is not lost
+ cache.resetAccessCount();
+ // if the memory used plus one large object is smaller than the
+ // allocated memory,
+ // then the memory was not fully used
+ wasFull = (memoryUsed + BIG_OBJECT_SIZE) >= memory;
+ }
+
+ boolean wasFull() {
+ return wasFull;
+ }
+
+ long getAccessCount() {
+ return accessCount;
+ }
+
+ long getMemoryUsed() {
+ return memoryUsed;
+ }
+
+ void setMemory(long mem) {
+ this.memory = mem;
+ }
+
+ long getMemory() {
+ return memory;
+ }
+
+ Cache getCache() {
+ return cache;
+ }
+
+ }
+
+ public void disposeCache(Cache cache) {
+ remove(cache);
+ }
+
+}
Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
===================================================================
--- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java (revision 472064)
+++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java (working copy)
@@ -45,6 +45,9 @@
import org.apache.jackrabbit.core.state.ItemStateException;
import org.apache.jackrabbit.core.persistence.PMContext;
import org.apache.jackrabbit.core.persistence.PersistenceManager;
+import org.apache.jackrabbit.core.state.CacheManager;
+import org.apache.jackrabbit.core.state.ItemStateCacheFactory;
+import org.apache.jackrabbit.core.state.ManagedMLRUItemStateCacheFactory;
import org.apache.jackrabbit.core.state.SharedItemStateManager;
import org.apache.jackrabbit.core.state.ChangeLog;
import org.apache.jackrabbit.core.version.VersionManager;
@@ -210,6 +213,16 @@
private final ReadWriteLock shutdownLock = new WriterPreferenceReadWriteLock();
/**
+ * There is one cache manager per repository that manages the sizes of the caches used.
+ */
+ private final CacheManager cacheMgr = new CacheManager();
+
+ /**
+ * There is only one item state cache factory
+ */
+ private final ItemStateCacheFactory cacheFactory = new ManagedMLRUItemStateCacheFactory(cacheMgr);
+
+ /**
* private constructor
*
* @param repConfig
@@ -321,6 +334,15 @@
}
/**
+ * Get the item state cache factory of this repository.
+ *
+ * @return the cache manager
+ */
+ public ItemStateCacheFactory getItemStateCacheFactory() {
+ return cacheFactory;
+ }
+
+ /**
* Creates the version manager.
*
* @param vConfig the versioning config
@@ -341,7 +363,7 @@
ntReg);
VersionManagerImpl vMgr = new VersionManagerImpl(pm, fs, ntReg, delegatingDispatcher,
- VERSION_STORAGE_NODE_ID, SYSTEM_ROOT_NODE_ID);
+ VERSION_STORAGE_NODE_ID, SYSTEM_ROOT_NODE_ID, cacheFactory);
if (clusterNode != null) {
vMgr.setEventChannel(clusterNode.createUpdateChannel());
}
@@ -1703,7 +1725,7 @@
// create item state manager
try {
itemStateMgr =
- new SharedItemStateManager(persistMgr, rootNodeId, ntReg, true);
+ new SharedItemStateManager(persistMgr, rootNodeId, ntReg, true, cacheFactory);
try {
itemStateMgr.addVirtualItemStateProvider(
vMgr.getVirtualItemStateProvider());
Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java
===================================================================
--- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java (revision 472064)
+++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java (working copy)
@@ -21,24 +21,25 @@
import org.apache.jackrabbit.core.NodeImpl;
import org.apache.jackrabbit.core.PropertyId;
import org.apache.jackrabbit.core.SessionImpl;
-import org.apache.jackrabbit.core.persistence.PersistenceManager;
+import org.apache.jackrabbit.core.cluster.UpdateEventChannel;
+import org.apache.jackrabbit.core.cluster.UpdateEventListener;
import org.apache.jackrabbit.core.fs.FileSystem;
import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
import org.apache.jackrabbit.core.observation.DelegatingObservationDispatcher;
import org.apache.jackrabbit.core.observation.EventStateCollection;
import org.apache.jackrabbit.core.observation.EventStateCollectionFactory;
+import org.apache.jackrabbit.core.persistence.PersistenceManager;
import org.apache.jackrabbit.core.state.ChangeLog;
+import org.apache.jackrabbit.core.state.ItemState;
+import org.apache.jackrabbit.core.state.ItemStateCacheFactory;
import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.state.ItemStateListener;
import org.apache.jackrabbit.core.state.LocalItemStateManager;
import org.apache.jackrabbit.core.state.NodeReferences;
import org.apache.jackrabbit.core.state.NodeReferencesId;
import org.apache.jackrabbit.core.state.NodeState;
import org.apache.jackrabbit.core.state.PropertyState;
import org.apache.jackrabbit.core.state.SharedItemStateManager;
-import org.apache.jackrabbit.core.state.ItemStateListener;
-import org.apache.jackrabbit.core.state.ItemState;
-import org.apache.jackrabbit.core.cluster.UpdateEventChannel;
-import org.apache.jackrabbit.core.cluster.UpdateEventListener;
import org.apache.jackrabbit.core.value.InternalValue;
import org.apache.jackrabbit.core.virtual.VirtualItemStateProvider;
import org.apache.jackrabbit.name.MalformedPathException;
@@ -129,7 +130,8 @@
public VersionManagerImpl(PersistenceManager pMgr, FileSystem fs,
NodeTypeRegistry ntReg,
DelegatingObservationDispatcher obsMgr, NodeId rootId,
- NodeId rootParentId) throws RepositoryException {
+ NodeId rootParentId,
+ ItemStateCacheFactory cacheFactory) throws RepositoryException {
try {
this.pMgr = pMgr;
this.fs = fs;
@@ -156,9 +158,9 @@
pMgr.store(cl);
}
sharedStateMgr =
- new VersionItemStateManager(pMgr, rootId, ntReg);
+ new VersionItemStateManager(pMgr, rootId, ntReg, cacheFactory);
- stateMgr = new LocalItemStateManager(sharedStateMgr, escFactory);
+ stateMgr = new LocalItemStateManager(sharedStateMgr, escFactory, cacheFactory);
stateMgr.addListener(this);
NodeState nodeState = (NodeState) stateMgr.getItemState(rootId);
@@ -515,9 +517,10 @@
public VersionItemStateManager(PersistenceManager persistMgr,
NodeId rootNodeId,
- NodeTypeRegistry ntReg)
+ NodeTypeRegistry ntReg,
+ ItemStateCacheFactory cacheFactory)
throws ItemStateException {
- super(persistMgr, rootNodeId, ntReg, false);
+ super(persistMgr, rootNodeId, ntReg, false, cacheFactory);
}
protected void checkReferentialIntegrity(ChangeLog changes)
Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersionManager.java
===================================================================
--- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersionManager.java (revision 472064)
+++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersionManager.java (working copy)
@@ -24,17 +24,18 @@
import org.apache.jackrabbit.core.TransactionContext;
import org.apache.jackrabbit.core.TransactionException;
import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
+import org.apache.jackrabbit.core.observation.EventStateCollection;
import org.apache.jackrabbit.core.observation.EventStateCollectionFactory;
-import org.apache.jackrabbit.core.observation.EventStateCollection;
import org.apache.jackrabbit.core.state.ChangeLog;
import org.apache.jackrabbit.core.state.ItemState;
+import org.apache.jackrabbit.core.state.ItemStateCacheFactory;
import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.state.ItemStateListener;
import org.apache.jackrabbit.core.state.NoSuchItemStateException;
import org.apache.jackrabbit.core.state.NodeReferences;
import org.apache.jackrabbit.core.state.NodeReferencesId;
import org.apache.jackrabbit.core.state.NodeState;
import org.apache.jackrabbit.core.state.XAItemStateManager;
-import org.apache.jackrabbit.core.state.ItemStateListener;
import org.apache.jackrabbit.core.virtual.VirtualItemStateProvider;
import org.apache.jackrabbit.core.virtual.VirtualNodeState;
import org.apache.jackrabbit.core.virtual.VirtualPropertyState;
@@ -96,14 +97,14 @@
* Creates a new instance of this class.
*/
public XAVersionManager(VersionManagerImpl vMgr, NodeTypeRegistry ntReg,
- SessionImpl session)
+ SessionImpl session, ItemStateCacheFactory cacheFactory)
throws RepositoryException {
this.vMgr = vMgr;
this.ntReg = ntReg;
this.session = session;
this.stateMgr = new XAItemStateManager(vMgr.getSharedStateMgr(),
- this, CHANGE_LOG_ATTRIBUTE_NAME);
+ this, CHANGE_LOG_ATTRIBUTE_NAME, cacheFactory);
NodeState state;
try {
@@ -199,6 +200,7 @@
* {@inheritDoc}
*/
public void close() throws Exception {
+ stateMgr.dispose();
}
//---------------------------------------------< VirtualItemStateProvider >
Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/WorkspaceImpl.java
===================================================================
--- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/WorkspaceImpl.java (revision 472064)
+++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/WorkspaceImpl.java (working copy)
@@ -21,13 +21,12 @@
import org.apache.jackrabbit.core.lock.LockManager;
import org.apache.jackrabbit.core.observation.EventStateCollection;
import org.apache.jackrabbit.core.observation.EventStateCollectionFactory;
-import org.apache.jackrabbit.core.observation.ObservationDispatcher;
import org.apache.jackrabbit.core.observation.ObservationManagerImpl;
import org.apache.jackrabbit.core.query.QueryManagerImpl;
import org.apache.jackrabbit.core.state.LocalItemStateManager;
import org.apache.jackrabbit.core.state.SharedItemStateManager;
+import org.apache.jackrabbit.core.version.DateVersionSelector;
import org.apache.jackrabbit.core.version.VersionImpl;
-import org.apache.jackrabbit.core.version.DateVersionSelector;
import org.apache.jackrabbit.core.version.VersionSelector;
import org.apache.jackrabbit.core.xml.ImportHandler;
import org.apache.jackrabbit.core.xml.Importer;
@@ -168,6 +167,7 @@
obsMgr.dispose();
obsMgr = null;
}
+ stateMgr.dispose();
}
/**
@@ -757,7 +757,7 @@
* @return local item state manager
*/
protected LocalItemStateManager createItemStateManager(SharedItemStateManager shared) {
- return new LocalItemStateManager(shared, this);
+ return new LocalItemStateManager(shared, this, rep.getItemStateCacheFactory());
}
//------------------------------------------< EventStateCollectionFactory >