package org.exist.collections; import org.exist.storage.BrokerPool; import org.exist.storage.CacheManager; import org.exist.storage.cache.Cacheable; import org.exist.storage.cache.LRDCache; import org.exist.storage.lock.Lock; import org.exist.util.hashtable.Long2ObjectHashMap; import org.exist.util.hashtable.Object2LongHashMap; import org.exist.xmldb.XmldbURI; /** * Global cache for {@link org.exist.collections.Collection} objects. The * cache is owned by {@link org.exist.storage.index.CollectionStore}. It is not * synchronized. Thus a lock should be obtained on the collection store before * accessing the cache. * * @author wolf */ public class CollectionCache extends LRDCache { private Object2LongHashMap names; private BrokerPool pool; public CollectionCache(BrokerPool pool, int blockBuffers, double growthThreshold) { super(blockBuffers, 1.8, growthThreshold, CacheManager.DATA_CACHE); this.names = new Object2LongHashMap(blockBuffers); this.pool = pool; setFileName("collections.dbx"); } public void add(Collection collection) { add(collection, 1); } public void add(Collection collection, int initialRefCount) { super.add(collection, initialRefCount); String name = collection.getURI().getRawCollectionPath(); names.put(name, collection.getKey()); } public Collection get(Collection collection) { return (Collection) get(collection.getKey()); } public Collection get(XmldbURI name) { long key = names.get(name.getRawCollectionPath()); if (key < 0) return null; return (Collection) get(key); } /** * Overwritten to lock collections before they are removed. */ protected Cacheable removeOne(Cacheable item) { Collection old; Lock lock; double rd = 0, minRd = -1; int bucket = -1; for (int i = 0; i < items.length; i++) { old = (Collection)items[i]; if (old == null) { bucket = i; break; } else { lock = old.getLock(); // calculate the reference density rd = old.getReferenceCount() / (double)(totalReferences - old.getTimestamp()); // attempt to acquire a read lock on the collection. // the collection is not considered for removal if the lock // cannot be acquired immediately. if(lock.attempt(Lock.READ_LOCK)) { if ((minRd < 0 || rd < minRd) && old.allowUnload()) { minRd = rd; bucket = i; } lock.release(Lock.READ_LOCK); } } } if (bucket < 0) bucket = 0; old = (Collection)items[bucket]; if (old != null) { if(pool.getConfigurationManager()!=null){ // might be null during db initialization pool.getConfigurationManager().invalidate(old.getURI()); } map.remove(old.getKey()); names.remove(old.getURI().getRawCollectionPath()); old.sync(true); } items[bucket] = item; map.put(item.getKey(), item); if (cacheManager != null) { cacheManager.requestMem(this); } return old; } public void remove(Cacheable item) { final Collection col = (Collection) item; super.remove(item); names.remove(col.getURI().getRawCollectionPath()); if(pool.getConfigurationManager() != null) // might be null during db initialization pool.getConfigurationManager().invalidate(col.getURI()); } /** * Compute and return the in-memory size of all collections * currently contained in this cache. * * @see org.exist.storage.CollectionCacheManager * @return in-memory size in bytes. */ public int getRealSize() { int size = 0; for (int i = 0; i < items.length; i++) { Collection item = (Collection) items[i]; if (item != null) size += item.getMemorySize(); } return size; } public void resize(int newSize) { if (newSize < size) { shrink(newSize); names = new Object2LongHashMap(newSize); } else { LOG.debug("Growing cache from " + size + " to " + newSize); Cacheable[] newItems = new Cacheable[newSize]; Long2ObjectHashMap newMap = new Long2ObjectHashMap(newSize); Object2LongHashMap newNames = new Object2LongHashMap(newSize); for (int i = 0; i < count; i++) { newItems[i] = items[i]; if (items[i] != null) { newMap.put(items[i].getKey(), items[i]); newNames.put(((Collection) items[i]).getURI().getRawCollectionPath(), items[i].getKey()); } } this.size = newSize; this.map = newMap; this.names = newNames; this.items = newItems; accounting.reset(); accounting.setTotalSize(size); } } }