Index: CHANGES.txt =================================================================== --- CHANGES.txt (revision 888398) +++ CHANGES.txt (working copy) @@ -8,6 +8,10 @@ class is no longer used by Lucene. (Gunnar Wagenknecht via Mike McCandless) +* LUCENE-2135: Added FieldCache.purge(IndexReader) method to the + interface. Anyone implementing FieldCache externally will need to + fix their code to implement this, on upgrading. (Mike McCandless) + Changes in runtime behavior API Changes @@ -75,6 +79,10 @@ This avoid the overhead of using a PQ unecessarily. (Mike McCandless) +* LUCENE-2135: On IndexReader.close, forcefully evict any entries from + the FieldCache rather than waiting for the WeakHashMap to release + the reference (Mike McCandless) + Build Test Cases Index: src/test/org/apache/lucene/index/TestIndexReader.java =================================================================== --- src/test/org/apache/lucene/index/TestIndexReader.java (revision 888398) +++ src/test/org/apache/lucene/index/TestIndexReader.java (working copy) @@ -1620,9 +1620,9 @@ // Clone reader IndexReader r2 = (IndexReader) r.clone(); + final int[] ints2 = FieldCache.DEFAULT.getInts(r2, "number"); r.close(); assertTrue(r2 != r); - final int[] ints2 = FieldCache.DEFAULT.getInts(r2, "number"); r2.close(); assertEquals(1, ints2.length); @@ -1656,9 +1656,9 @@ // Reopen reader1 --> reader2 IndexReader r2 = r.reopen(); - r.close(); IndexReader sub0 = r2.getSequentialSubReaders()[0]; final int[] ints2 = FieldCache.DEFAULT.getInts(sub0, "number"); + r.close(); r2.close(); assertTrue(ints == ints2); @@ -1694,10 +1694,10 @@ // Reopen reader1 --> reader2 IndexReader r2 = r.reopen(true); - r.close(); assertTrue(r2 instanceof ReadOnlyDirectoryReader); IndexReader[] subs = r2.getSequentialSubReaders(); final int[] ints2 = FieldCache.DEFAULT.getInts(subs[0], "number"); + r.close(); r2.close(); assertTrue(subs[0] instanceof ReadOnlySegmentReader); Index: src/java/org/apache/lucene/search/FieldCache.java =================================================================== --- src/java/org/apache/lucene/search/FieldCache.java (revision 888398) +++ src/java/org/apache/lucene/search/FieldCache.java (working copy) @@ -594,6 +594,15 @@ public abstract void purgeAllCaches(); /** + * Expert: drops all cache entries associated with this + * reader. NOTE: this reader must precisely match the + * reader that the cache entry is keyed on. If you pass a + * top-level reader, it usually will have no effect as + * Lucene now caches at the segment reader level. + */ + public abstract void purge(IndexReader r); + + /** * If non-null, FieldCacheImpl will warn whenever * entries are created that are not sane according to * {@link org.apache.lucene.util.FieldCacheSanityChecker}. Index: src/java/org/apache/lucene/search/FieldCacheImpl.java =================================================================== --- src/java/org/apache/lucene/search/FieldCacheImpl.java (revision 888398) +++ src/java/org/apache/lucene/search/FieldCacheImpl.java (working copy) @@ -61,6 +61,12 @@ public void purgeAllCaches() { init(); } + + public void purge(IndexReader r) { + for(Cache c : caches.values()) { + c.purge(r); + } + } public CacheEntry[] getCacheEntries() { List result = new ArrayList(17); @@ -144,6 +150,14 @@ protected abstract Object createValue(IndexReader reader, Entry key) throws IOException; + /** Remove this reader from the cache, if present. */ + public void purge(IndexReader r) { + Object readerKey = r.getFieldCacheKey(); + synchronized(readerCache) { + readerCache.remove(readerKey); + } + } + public Object get(IndexReader reader, Entry key) throws IOException { Map innerCache; Object value; Index: src/java/org/apache/lucene/index/SegmentReader.java =================================================================== --- src/java/org/apache/lucene/index/SegmentReader.java (revision 888398) +++ src/java/org/apache/lucene/index/SegmentReader.java (working copy) @@ -38,6 +38,8 @@ import org.apache.lucene.util.BitVector; import org.apache.lucene.util.CloseableThreadLocal; +import org.apache.lucene.search.FieldCache; // not great (circular); used only to purge FieldCache entry on close + /** @version $Id */ /** *

NOTE: This API is new and still experimental @@ -92,16 +94,19 @@ final int readBufferSize; final int termsIndexDivisor; + private final SegmentReader origInstance; + TermInfosReader tis; FieldsReader fieldsReaderOrig; TermVectorsReader termVectorsReaderOrig; CompoundFileReader cfsReader; CompoundFileReader storeCFSReader; - CoreReaders(Directory dir, SegmentInfo si, int readBufferSize, int termsIndexDivisor) throws IOException { + CoreReaders(SegmentReader origInstance, Directory dir, SegmentInfo si, int readBufferSize, int termsIndexDivisor) throws IOException { segment = si.name; this.readBufferSize = readBufferSize; this.dir = dir; + this.origInstance = origInstance; boolean success = false; @@ -232,6 +237,11 @@ if (storeCFSReader != null) { storeCFSReader.close(); } + + // Force FieldCache to evict our entries at this point + if (freqStream != null) { + FieldCache.DEFAULT.purge(origInstance); + } } } @@ -573,7 +583,7 @@ boolean success = false; try { - instance.core = new CoreReaders(dir, si, readBufferSize, termInfosIndexDivisor); + instance.core = new CoreReaders(instance, dir, si, readBufferSize, termInfosIndexDivisor); if (doOpenStores) { instance.core.openDocStores(si); }