Index: CHANGES.txt
===================================================================
--- CHANGES.txt (revision 687039)
+++ CHANGES.txt (working copy)
@@ -108,6 +108,14 @@
16. LUCENE-1334: Add new constructor for Term: Term(String fieldName)
which defaults term text to "". (DM Smith via Mike McCandless)
+17. LUCENE-1329: Add optional readOnly boolean when opening an
+ IndexReader. A readOnly reader is not allowed to make changes
+ (deletions, norms) to the index; in exchanged, the isDeleted
+ method, often a bottleneck when searching with many threads, is
+ not synchronized. The default for readOnly is still false, but in
+ 3.0 the default will become true. (Jason Rutherglen via Mike
+ McCandless)
+
Bug fixes
1. LUCENE-1134: Fixed BooleanQuery.rewrite to only optimize a single
Index: src/test/org/apache/lucene/index/TestIndexReader.java
===================================================================
--- src/test/org/apache/lucene/index/TestIndexReader.java (revision 687039)
+++ src/test/org/apache/lucene/index/TestIndexReader.java (working copy)
@@ -1329,4 +1329,61 @@
r2.close();
d.close();
}
+
+ public void testReadOnly() throws Throwable {
+ RAMDirectory d = new MockRAMDirectory();
+ IndexWriter writer = new IndexWriter(d, new StandardAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED);
+ addDocumentWithFields(writer);
+ writer.commit();
+ addDocumentWithFields(writer);
+ writer.close();
+
+ IndexReader r = IndexReader.open(d, true);
+ try {
+ r.deleteDocument(0);
+ fail();
+ } catch (UnsupportedOperationException uoe) {
+ // expected
+ }
+
+ writer = new IndexWriter(d, new StandardAnalyzer(), false, IndexWriter.MaxFieldLength.LIMITED);
+ addDocumentWithFields(writer);
+ writer.close();
+
+ // Make sure reopen is still readonly:
+ IndexReader r2 = r.reopen();
+ r.close();
+
+ assertFalse(r == r2);
+
+ try {
+ r2.deleteDocument(0);
+ fail();
+ } catch (UnsupportedOperationException uoe) {
+ // expected
+ }
+
+ writer = new IndexWriter(d, new StandardAnalyzer(), false, IndexWriter.MaxFieldLength.LIMITED);
+ writer.optimize();
+ writer.close();
+
+ // Make sure reopen to a single segment is still readonly:
+ IndexReader r3 = r2.reopen();
+ r2.close();
+
+ assertFalse(r == r2);
+
+ try {
+ r3.deleteDocument(0);
+ fail();
+ } catch (UnsupportedOperationException uoe) {
+ // expected
+ }
+
+ // Make sure write lock isn't held
+ writer = new IndexWriter(d, new StandardAnalyzer(), false, IndexWriter.MaxFieldLength.LIMITED);
+ writer.close();
+
+ r3.close();
+ }
}
Index: src/java/org/apache/lucene/index/DirectoryIndexReader.java
===================================================================
--- src/java/org/apache/lucene/index/DirectoryIndexReader.java (revision 687039)
+++ src/java/org/apache/lucene/index/DirectoryIndexReader.java (working copy)
@@ -43,21 +43,24 @@
private SegmentInfos segmentInfos;
private Lock writeLock;
private boolean stale;
- private HashSet synced = new HashSet();
+ private final HashSet synced = new HashSet();
/** Used by commit() to record pre-commit state in case
* rollback is necessary */
private boolean rollbackHasChanges;
private SegmentInfos rollbackSegmentInfos;
+ protected boolean readOnly;
+
- void init(Directory directory, SegmentInfos segmentInfos, boolean closeDirectory)
+ void init(Directory directory, SegmentInfos segmentInfos, boolean closeDirectory, boolean readOnly)
throws IOException {
this.directory = directory;
this.segmentInfos = segmentInfos;
this.closeDirectory = closeDirectory;
+ this.readOnly = readOnly;
- if (segmentInfos != null) {
+ if (!readOnly && segmentInfos != null) {
// We assume that this segments_N was previously
// properly sync'd:
for(int i=0;i
+ + NOTE: as of 2.4, it's possible to open a read-only + IndexReader using one of the static open methods that + accepts the boolean readOnly parameter. Such a reader has + better concurrency as it's not necessary to synchronize on + the isDeleted method. Currently the default for readOnly + is false, meaning if not specified you will get a + read/write IndexReader. But in 3.0 this default will + change to true, meaning you must explicitly specify false + if you want to make changes with the resulting IndexReader. +
+ @version $Id$ */ public abstract class IndexReader { + // NOTE: in 3.0 this will change to true + final static boolean READ_ONLY_DEFAULT = false; + /** * Constants describing field properties, for example used for * {@link IndexReader#getFieldNames(FieldOption)}. @@ -181,46 +197,61 @@ } } - /** Returns an IndexReader reading the index in an FSDirectory in the named - path. + /** Returns a read/write IndexReader reading the index in an FSDirectory in the named + path. NOTE: starting in 3.0 this will return a readOnly IndexReader. * @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error * @param path the path to the index directory */ public static IndexReader open(String path) throws CorruptIndexException, IOException { - return open(FSDirectory.getDirectory(path), true, null, null); + return open(FSDirectory.getDirectory(path), true, null, null, READ_ONLY_DEFAULT); } - /** Returns an IndexReader reading the index in an FSDirectory in the named - * path. + /** Returns a read/write IndexReader reading the index in an FSDirectory in the named + * path. NOTE: starting in 3.0 this will return a readOnly IndexReader. * @param path the path to the index directory * @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error */ public static IndexReader open(File path) throws CorruptIndexException, IOException { - return open(FSDirectory.getDirectory(path), true, null, null); + return open(FSDirectory.getDirectory(path), true, null, null, READ_ONLY_DEFAULT); } - /** Returns an IndexReader reading the index in the given Directory. + /** Returns a read/write IndexReader reading the index in + * the given Directory. NOTE: starting in 3.0 this + * will return a readOnly IndexReader. * @param directory the index directory * @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error */ public static IndexReader open(final Directory directory) throws CorruptIndexException, IOException { - return open(directory, false, null, null); + return open(directory, false, null, null, READ_ONLY_DEFAULT); } - /** Expert: returns an IndexReader reading the index in the given - * {@link IndexCommit}. + /** Returns a read/write or read only IndexReader reading the index in the given Directory. + * @param directory the index directory + * @param readOnly true if no changes (deletions, norms) will be made with this IndexReader + * @throws CorruptIndexException if the index is corrupt + * @throws IOException if there is a low-level IO error + */ + public static IndexReader open(final Directory directory, boolean readOnly) throws CorruptIndexException, IOException { + return open(directory, false, null, null, readOnly); + } + + /** Expert: returns a read/write IndexReader reading the index in the given + * {@link IndexCommit}. NOTE: starting in 3.0 this + * will return a readOnly IndexReader. * @param commit the commit point to open * @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error */ public static IndexReader open(final IndexCommit commit) throws CorruptIndexException, IOException { - return open(commit.getDirectory(), false, null, commit); + return open(commit.getDirectory(), false, null, commit, READ_ONLY_DEFAULT); } - /** Expert: returns an IndexReader reading the index in the given + /** Expert: returns a read/write IndexReader reading the index in the given * Directory, with a custom {@link IndexDeletionPolicy}. + * NOTE: starting in 3.0 this will return a + * readOnly IndexReader. * @param directory the index directory * @param deletionPolicy a custom deletion policy (only used * if you use this reader to perform deletes or to set @@ -229,11 +260,29 @@ * @throws IOException if there is a low-level IO error */ public static IndexReader open(final Directory directory, IndexDeletionPolicy deletionPolicy) throws CorruptIndexException, IOException { - return open(directory, false, deletionPolicy, null); + return open(directory, false, deletionPolicy, null, READ_ONLY_DEFAULT); } - /** Expert: returns an IndexReader reading the index in the given - * Directory, using a specific commit and with a custom {@link IndexDeletionPolicy}. + /** Expert: returns a read/write or read only IndexReader reading the index in the given + * Directory, with a custom {@link IndexDeletionPolicy}. + * NOTE: starting in 3.0 this will return a + * readOnly IndexReader. + * @param directory the index directory + * @param deletionPolicy a custom deletion policy (only used + * if you use this reader to perform deletes or to set + * norms); see {@link IndexWriter} for details. + * @param readOnly true if no changes (deletions, norms) will be made with this IndexReader + * @throws CorruptIndexException if the index is corrupt + * @throws IOException if there is a low-level IO error + */ + public static IndexReader open(final Directory directory, IndexDeletionPolicy deletionPolicy, boolean readOnly) throws CorruptIndexException, IOException { + return open(directory, false, deletionPolicy, null, readOnly); + } + + /** Expert: returns a read/write IndexReader reading the index in the given + * Directory, using a specific commit and with a custom + * {@link IndexDeletionPolicy}. NOTE: starting in + * 3.0 this will return a readOnly IndexReader. * @param commit the specific {@link IndexCommit} to open; * see {@link IndexReader#listCommits} to list all commits * in a directory @@ -244,13 +293,29 @@ * @throws IOException if there is a low-level IO error */ public static IndexReader open(final IndexCommit commit, IndexDeletionPolicy deletionPolicy) throws CorruptIndexException, IOException { - return open(commit.getDirectory(), false, deletionPolicy, commit); + return open(commit.getDirectory(), false, deletionPolicy, commit, READ_ONLY_DEFAULT); } - private static IndexReader open(final Directory directory, final boolean closeDirectory, final IndexDeletionPolicy deletionPolicy, final IndexCommit commit) throws CorruptIndexException, IOException { - return DirectoryIndexReader.open(directory, closeDirectory, deletionPolicy, commit); + /** Expert: returns a read/write or read only IndexReader reading the index in the given + * Directory, using a specific commit and with a custom {@link IndexDeletionPolicy}. + * @param commit the specific {@link IndexCommit} to open; + * see {@link IndexReader#listCommits} to list all commits + * in a directory + * @param deletionPolicy a custom deletion policy (only used + * if you use this reader to perform deletes or to set + * norms); see {@link IndexWriter} for details. + * @param readOnly true if no changes (deletions, norms) will be made with this IndexReader + * @throws CorruptIndexException if the index is corrupt + * @throws IOException if there is a low-level IO error + */ + public static IndexReader open(final IndexCommit commit, IndexDeletionPolicy deletionPolicy, boolean readOnly) throws CorruptIndexException, IOException { + return open(commit.getDirectory(), false, deletionPolicy, commit, readOnly); } + private static IndexReader open(final Directory directory, final boolean closeDirectory, final IndexDeletionPolicy deletionPolicy, final IndexCommit commit, final boolean readOnly) throws CorruptIndexException, IOException { + return DirectoryIndexReader.open(directory, closeDirectory, deletionPolicy, commit, readOnly); + } + /** * Refreshes an IndexReader if the index has changed since this instance * was (re)opened. @@ -637,7 +702,7 @@ * be obtained) * @throws IOException if there is a low-level IO error */ - public final synchronized void setNorm(int doc, String field, byte value) + public synchronized void setNorm(int doc, String field, byte value) throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException { ensureOpen(); acquireWriteLock(); @@ -762,7 +827,7 @@ * be obtained) * @throws IOException if there is a low-level IO error */ - public final synchronized void deleteDocument(int docNum) throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException { + public synchronized void deleteDocument(int docNum) throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException { ensureOpen(); acquireWriteLock(); hasChanges = true; @@ -793,7 +858,7 @@ * be obtained) * @throws IOException if there is a low-level IO error */ - public final int deleteDocuments(Term term) throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException { + public int deleteDocuments(Term term) throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException { ensureOpen(); TermDocs docs = termDocs(term); if (docs == null) return 0; @@ -819,7 +884,7 @@ * @throws CorruptIndexException if the index is corrupt * @throws IOException if there is a low-level IO error */ - public final synchronized void undeleteAll() throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException { + public synchronized void undeleteAll() throws StaleReaderException, CorruptIndexException, LockObtainFailedException, IOException { ensureOpen(); acquireWriteLock(); hasChanges = true; Index: src/java/org/apache/lucene/index/MultiSegmentReader.java =================================================================== --- src/java/org/apache/lucene/index/MultiSegmentReader.java (revision 687039) +++ src/java/org/apache/lucene/index/MultiSegmentReader.java (working copy) @@ -42,8 +42,9 @@ private boolean hasDeletions = false; /** Construct reading the named set of readers. */ - MultiSegmentReader(Directory directory, SegmentInfos sis, boolean closeDirectory) throws IOException { - super(directory, sis, closeDirectory); + MultiSegmentReader(Directory directory, SegmentInfos sis, boolean closeDirectory, boolean readOnly) throws IOException { + super(directory, sis, closeDirectory, readOnly); + // To reduce the chance of hitting FileNotFound // (and having to retry), we open segments in // reverse because IndexWriter merges & deletes @@ -52,7 +53,7 @@ SegmentReader[] readers = new SegmentReader[sis.size()]; for (int i = sis.size()-1; i >= 0; i--) { try { - readers[i] = SegmentReader.get(sis.info(i)); + readers[i] = SegmentReader.get(readOnly, sis.info(i)); } catch (IOException e) { // Close all readers we had opened: for(i++;i