Index: lucene/contrib/misc/src/test/org/apache/lucene/search/TestSearcherManager.java =================================================================== --- lucene/contrib/misc/src/test/org/apache/lucene/search/TestSearcherManager.java (revision 1183650) +++ lucene/contrib/misc/src/test/org/apache/lucene/search/TestSearcherManager.java (working copy) @@ -18,16 +18,17 @@ */ import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; -import java.util.List; -import java.util.ArrayList; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.lucene.analysis.MockAnalyzer; import org.apache.lucene.document.Document; +import org.apache.lucene.index.ConcurrentMergeScheduler; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.Term; import org.apache.lucene.index.ThreadedIndexingAndSearchingTestCase; @@ -63,7 +64,6 @@ @Override protected void doAfterWriter(ExecutorService es) throws Exception { - // SearcherManager needs to see empty commit: final SearcherWarmer warmer = new SearcherWarmer() { @Override public void warm(IndexSearcher s) throws IOException { @@ -75,6 +75,7 @@ mgr = SearcherManager.open(writer, true, warmer, es); isNRT = true; } else { + // SearcherManager needs to see empty commit: writer.commit(); mgr = SearcherManager.open(dir, warmer, es); isNRT = false; @@ -178,8 +179,9 @@ public void testIntermediateClose() throws IOException, InterruptedException { Directory dir = newDirectory(); + // Test can deadlock if we use SMS: IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig( - TEST_VERSION_CURRENT, new MockAnalyzer(random))); + TEST_VERSION_CURRENT, new MockAnalyzer(random)).setMergeScheduler(new ConcurrentMergeScheduler())); writer.addDocument(new Document()); writer.commit(); final CountDownLatch awaitEnterWarm = new CountDownLatch(1); Index: lucene/contrib/misc/src/java/org/apache/lucene/search/SearcherLifetimeManager.java =================================================================== --- lucene/contrib/misc/src/java/org/apache/lucene/search/SearcherLifetimeManager.java (revision 1183650) +++ lucene/contrib/misc/src/java/org/apache/lucene/search/SearcherLifetimeManager.java (working copy) @@ -173,8 +173,8 @@ // incRef done by SearcherTracker ctor: tracker.close(); } - } else { - assert tracker.searcher == searcher; + } else if (tracker.searcher != searcher) { + throw new IllegalArgumentException("the provided searcher has the same underlying reader version yet the searcher instance differs from before (new=" + searcher + " vs old=" + tracker.searcher); } return version; Index: lucene/contrib/misc/src/java/org/apache/lucene/search/SearcherManager.java =================================================================== --- lucene/contrib/misc/src/java/org/apache/lucene/search/SearcherManager.java (revision 1183650) +++ lucene/contrib/misc/src/java/org/apache/lucene/search/SearcherManager.java (working copy) @@ -292,7 +292,7 @@ @Override protected IndexReader openIfChanged(IndexReader oldReader) throws IOException { - return IndexReader.openIfChanged(oldReader, writer, applyDeletes); + return IndexReader.openIfChanged(oldReader, applyDeletes); } } Index: lucene/src/test/org/apache/lucene/search/TestCachingWrapperFilter.java =================================================================== --- lucene/src/test/org/apache/lucene/search/TestCachingWrapperFilter.java (revision 1183650) +++ lucene/src/test/org/apache/lucene/search/TestCachingWrapperFilter.java (working copy) @@ -235,10 +235,9 @@ // make sure we get a cache hit when we reopen reader // that had no change to deletions + writer.deleteDocuments(new Term("foo", "bar")); reader = refreshReader(reader); - assertTrue(reader != oldReader); - searcher.close(); - searcher = newSearcher(reader, false); + assertTrue(reader == oldReader); int missCount = filter.missCount; docs = searcher.search(constantScore, 1); assertEquals("[just filter] Should find a hit...", 1, docs.totalHits); Index: lucene/src/test/org/apache/lucene/search/TestCachingSpanFilter.java =================================================================== --- lucene/src/test/org/apache/lucene/search/TestCachingSpanFilter.java (revision 1183650) +++ lucene/src/test/org/apache/lucene/search/TestCachingSpanFilter.java (working copy) @@ -116,10 +116,10 @@ // make sure we get a cache hit when we reopen readers // that had no new deletions + // Deletes nothing: + writer.deleteDocuments(new Term("foo", "bar")); reader = refreshReader(reader); - assertTrue(reader != oldReader); - searcher.close(); - searcher = newSearcher(reader, false); + assertTrue(reader == oldReader); int missCount = filter.missCount; docs = searcher.search(constantScore, 1); assertEquals("[just filter] Should find a hit...", 1, docs.totalHits); Index: lucene/src/test/org/apache/lucene/index/TestIndexWriterReader.java =================================================================== --- lucene/src/test/org/apache/lucene/index/TestIndexWriterReader.java (revision 1183650) +++ lucene/src/test/org/apache/lucene/index/TestIndexWriterReader.java (working copy) @@ -857,7 +857,7 @@ int sum = 0; while(System.currentTimeMillis() < endTime) { IndexReader r2 = IndexReader.openIfChanged(r); - if (r2 != r) { + if (r2 != null) { r.close(); r = r2; } @@ -1016,4 +1016,40 @@ } } + public void testReopenAfterNoRealChange() throws Exception { + Directory d = newDirectory(); + IndexWriter w = new IndexWriter( + d, + newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random))); + w.setInfoStream(VERBOSE ? System.out : null); + + IndexReader r = w.getReader(); // start pooling readers + + IndexReader r2 = IndexReader.openIfChanged(r); + assertNull(r2); + + w.addDocument(new Document()); + IndexReader r3 = IndexReader.openIfChanged(r); + assertNotNull(r3); + assertTrue(r3.getVersion() != r.getVersion()); + assertTrue(r3.isCurrent()); + + // Deletes nothing in reality...: + w.deleteDocuments(new Term("foo", "bar")); + + // ... but IW marks this as not current: + assertFalse(r3.isCurrent()); + IndexReader r4 = IndexReader.openIfChanged(r3); + assertNull(r4); + + // Deletes nothing in reality...: + w.deleteDocuments(new Term("foo", "bar")); + IndexReader r5 = IndexReader.openIfChanged(r3, w, true); + assertNull(r5); + + r3.close(); + + w.close(); + d.close(); + } } Index: lucene/src/java/org/apache/lucene/index/DirectoryReader.java =================================================================== --- lucene/src/java/org/apache/lucene/index/DirectoryReader.java (revision 1183650) +++ lucene/src/java/org/apache/lucene/index/DirectoryReader.java (working copy) @@ -406,8 +406,15 @@ return doOpenIfChanged(true, commit); } - // NOTE: always returns a non-null result (ie new reader) - // but that could change someday + @Override + protected final IndexReader doOpenIfChanged(IndexWriter writer, boolean applyAllDeletes) throws CorruptIndexException, IOException { + if (writer == this.writer && applyAllDeletes == this.applyAllDeletes) { + return doOpenIfChanged(); + } else { + return super.doOpenIfChanged(writer, applyAllDeletes); + } + } + private final IndexReader doOpenFromWriter(boolean openReadOnly, IndexCommit commit) throws CorruptIndexException, IOException { assert readOnly; @@ -419,10 +426,18 @@ throw new IllegalArgumentException("a reader obtained from IndexWriter.getReader() cannot currently accept a commit"); } - // TODO: right now we *always* make a new reader; in - // the future we could have write make some effort to - // detect that no changes have occurred + if (writer.nrtIsCurrent(segmentInfos)) { + return null; + } + IndexReader reader = writer.getReader(applyAllDeletes); + + // If in fact no changes took place, return null: + if (reader.getVersion() == getVersion()) { + reader.decRef(); + return null; + } + reader.readerFinishedListeners = readerFinishedListeners; return reader; } Index: lucene/src/java/org/apache/lucene/index/IndexReader.java =================================================================== --- lucene/src/java/org/apache/lucene/index/IndexReader.java (revision 1183650) +++ lucene/src/java/org/apache/lucene/index/IndexReader.java (working copy) @@ -561,10 +561,6 @@ * with the old reader uses "copy on write" semantics to * ensure the changes are not seen by other readers. * - *
NOTE: If the provided reader is a near real-time - * reader, this method will return another near-real-time - * reader. - * * @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error * @return null if there are no changes; else, a new Index: lucene/src/java/org/apache/lucene/index/IndexWriter.java =================================================================== --- lucene/src/java/org/apache/lucene/index/IndexWriter.java (revision 1183650) +++ lucene/src/java/org/apache/lucene/index/IndexWriter.java (working copy) @@ -4073,6 +4073,7 @@ synchronized boolean nrtIsCurrent(SegmentInfos infos) { //System.out.println("IW.nrtIsCurrent " + (infos.version == segmentInfos.version && !docWriter.anyChanges() && !bufferedDeletesStream.any())); + ensureOpen(); return infos.version == segmentInfos.version && !docWriter.anyChanges() && !bufferedDeletesStream.any(); } Index: lucene/src/test-framework/org/apache/lucene/index/ThreadedIndexingAndSearchingTestCase.java =================================================================== --- lucene/src/test-framework/org/apache/lucene/index/ThreadedIndexingAndSearchingTestCase.java (revision 1183650) +++ lucene/src/test-framework/org/apache/lucene/index/ThreadedIndexingAndSearchingTestCase.java (working copy) @@ -55,7 +55,7 @@ // TODO // - mix in optimize, addIndexes -// - randomoly mix in non-congruent docs +// - randomly mix in non-congruent docs /** Utility class that spawns multiple indexing and * searching threads. */