From 75d6fab25bf9675a4d168ba72ca4d26760fd7f70 Mon Sep 17 00:00:00 2001 From: Michael McCandless Date: Wed, 11 Mar 2009 17:32:37 +0000 Subject: [PATCH] LUCENE-1551: add expert IndexReader.reopen(IndexCommit) git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@752538 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 5 ++ .../lucene/index/DirectoryIndexReader.java | 67 +++++++++++++------ .../org/apache/lucene/index/IndexReader.java | 10 +++ .../lucene/index/TestIndexReaderReopen.java | 62 +++++++++++++++++ 4 files changed, 122 insertions(+), 22 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 9bde26bd806..f8db1a3aed8 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -173,6 +173,11 @@ New features 15. LUCENE-1398: Add ReverseStringFilter to contrib/analyzers, a filter to reverse the characters in each token. (Koji Sekiguchi via yonik) +16. LUCENE-1551: Add expert IndexReader.reopen(IndexCommit) to allow + efficiently opening a new reader on a specific commit, sharing + resources with the original reader. (Torin Danil via Mike + McCandless) + Optimizations 1. LUCENE-1427: Fixed QueryWrapperFilter to not waste time computing diff --git a/src/java/org/apache/lucene/index/DirectoryIndexReader.java b/src/java/org/apache/lucene/index/DirectoryIndexReader.java index 212e267d32a..f6dc5d4e998 100644 --- a/src/java/org/apache/lucene/index/DirectoryIndexReader.java +++ b/src/java/org/apache/lucene/index/DirectoryIndexReader.java @@ -149,11 +149,15 @@ abstract class DirectoryIndexReader extends IndexReader implements Cloneable { public final synchronized IndexReader reopen() throws CorruptIndexException, IOException { // Preserve current readOnly - return doReopen(readOnly); + return doReopen(readOnly, null); } public final synchronized IndexReader reopen(boolean openReadOnly) throws CorruptIndexException, IOException { - return doReopen(openReadOnly); + return doReopen(openReadOnly, null); + } + + public final synchronized IndexReader reopen(final IndexCommit commit) throws CorruptIndexException, IOException { + return doReopen(true, commit); } public final synchronized Object clone() { @@ -194,29 +198,44 @@ abstract class DirectoryIndexReader extends IndexReader implements Cloneable { // If there are no changes to the index, simply return // ourself. If there are changes, load the latest // SegmentInfos and reopen based on that - protected final synchronized IndexReader doReopen(final boolean openReadOnly) throws CorruptIndexException, IOException { + protected final synchronized IndexReader doReopen(final boolean openReadOnly, IndexCommit commit) throws CorruptIndexException, IOException { ensureOpen(); - if (hasChanges) { - // We have changes, which means we are not readOnly: - assert readOnly == false; - // and we hold the write lock: - assert writeLock != null; - // so no other writer holds the write lock, which - // means no changes could have been done to the index: - assert isCurrent(); + assert commit == null || openReadOnly; - if (openReadOnly) { - return (IndexReader) clone(openReadOnly); - } else { - return this; + if (commit == null) { + if (hasChanges) { + // We have changes, which means we are not readOnly: + assert readOnly == false; + // and we hold the write lock: + assert writeLock != null; + // so no other writer holds the write lock, which + // means no changes could have been done to the index: + assert isCurrent(); + + if (openReadOnly) { + return (IndexReader) clone(openReadOnly); + } else { + return this; + } + } else if (isCurrent()) { + if (openReadOnly != readOnly) { + // Just fallback to clone + return (IndexReader) clone(openReadOnly); + } else { + return this; + } } - } else if (isCurrent()) { - if (openReadOnly != readOnly) { - // Just fallback to clone - return (IndexReader) clone(openReadOnly); - } else { - return this; + } else { + if (directory != commit.getDirectory()) + throw new IOException("the specified commit does not match the specified Directory"); + if (segmentInfos != null && commit.getSegmentsFileName().equals(segmentInfos.getCurrentSegmentFileName())) { + if (readOnly != openReadOnly) { + // Just fallback to clone + return (IndexReader) clone(openReadOnly); + } else { + return this; + } } } @@ -247,7 +266,11 @@ abstract class DirectoryIndexReader extends IndexReader implements Cloneable { closeDirectory = false; try { - reader = (DirectoryIndexReader) finder.run(); + if (commit == null) { + reader = (DirectoryIndexReader) finder.run(); + } else { + reader = (DirectoryIndexReader) finder.doBody(commit.getSegmentsFileName()); + } } finally { if (myCloseDirectory) { assert directory instanceof FSDirectory; diff --git a/src/java/org/apache/lucene/index/IndexReader.java b/src/java/org/apache/lucene/index/IndexReader.java index 7820a50b54b..a71bed71b07 100644 --- a/src/java/org/apache/lucene/index/IndexReader.java +++ b/src/java/org/apache/lucene/index/IndexReader.java @@ -369,6 +369,16 @@ public abstract class IndexReader implements Cloneable { throw new UnsupportedOperationException("This reader does not support reopen()."); } + /** Expert: reopen this reader on a specific commit point. + * This always returns a readOnly reader. If the + * specified commit point matches what this reader is + * already on, and this reader is already readOnly, then + * this same instance is returned; if it is not already + * readOnly, a readOnly clone is returned. */ + public synchronized IndexReader reopen(final IndexCommit commit) throws CorruptIndexException, IOException { + throw new UnsupportedOperationException("This reader does not support reopen(IndexCommit)."); + } + /** * Efficiently clones the IndexReader (sharing most * internal state). diff --git a/src/test/org/apache/lucene/index/TestIndexReaderReopen.java b/src/test/org/apache/lucene/index/TestIndexReaderReopen.java index 78a36d9efe0..d44746272b3 100644 --- a/src/test/org/apache/lucene/index/TestIndexReaderReopen.java +++ b/src/test/org/apache/lucene/index/TestIndexReaderReopen.java @@ -1052,6 +1052,7 @@ public class TestIndexReaderReopen extends LuceneTestCase { } } + /* private void assertReaderOpen(IndexReader reader) { reader.ensureOpen(); @@ -1062,6 +1063,7 @@ public class TestIndexReaderReopen extends LuceneTestCase { } } } + */ private void assertRefCountEquals(int refCount, IndexReader reader) { assertEquals("Reader has wrong refCount value.", refCount, reader.getRefCount()); @@ -1225,4 +1227,64 @@ public class TestIndexReaderReopen extends LuceneTestCase { r2.close(); dir.close(); } + + private static class KeepAllCommits implements IndexDeletionPolicy { + public void onInit(List commits) { + } + public void onCommit(List commits) { + } + } + + public void testReopenOnCommit() throws Throwable { + Directory dir = new MockRAMDirectory(); + IndexWriter writer = new IndexWriter(dir, new WhitespaceAnalyzer(), new KeepAllCommits(), IndexWriter.MaxFieldLength.UNLIMITED); + for(int i=0;i<4;i++) { + Document doc = new Document(); + doc.add(new Field("id", ""+i, Field.Store.NO, Field.Index.NOT_ANALYZED)); + writer.addDocument(doc); + writer.commit(""+i); + } + for(int i=0;i<4;i++) { + writer.deleteDocuments(new Term("id", ""+i)); + writer.commit(""+(4+i)); + } + writer.close(); + + IndexReader r = IndexReader.open(dir); + assertEquals(0, r.numDocs()); + assertEquals(4, r.maxDoc()); + + Iterator it = IndexReader.listCommits(dir).iterator(); + while(it.hasNext()) { + IndexCommit commit = (IndexCommit) it.next(); + IndexReader r2 = r.reopen(commit); + assertTrue(r2 != r); + + // Reader should be readOnly + try { + r2.deleteDocument(0); + fail("no exception hit"); + } catch (UnsupportedOperationException uoe) { + // expected + } + + final String s = commit.getUserData(); + final int v; + if (s == null) { + // First commit created by IW + v = -1; + } else { + v = Integer.parseInt(s); + } + if (v < 4) { + assertEquals(1+v, r2.numDocs()); + } else { + assertEquals(7-v, r2.numDocs()); + } + r.close(); + r = r2; + } + r.close(); + dir.close(); + } }