LUCENE-1329: enable read-only IndexReaders; the default is to get a read-write IndexReader on open, but in 3.0 this will change to read-only

git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@688323 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Michael McCandless 2008-08-23 13:47:18 +00:00
parent 59b8dca69a
commit f0d2a151ca
9 changed files with 318 additions and 61 deletions

View File

@ -114,6 +114,14 @@ API Changes
hashCode() and equals() in Token, and fixed all core and contrib
analyzers to use the re-use APIs. (DM Smith via Mike McCandless)
18. 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

View File

@ -43,21 +43,24 @@ abstract class DirectoryIndexReader extends IndexReader {
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<segmentInfos.size();i++) {
@ -72,16 +75,16 @@ abstract class DirectoryIndexReader extends IndexReader {
protected DirectoryIndexReader() {}
DirectoryIndexReader(Directory directory, SegmentInfos segmentInfos,
boolean closeDirectory) throws IOException {
boolean closeDirectory, boolean readOnly) throws IOException {
super();
init(directory, segmentInfos, closeDirectory);
init(directory, segmentInfos, closeDirectory, readOnly);
}
static DirectoryIndexReader open(final Directory directory, final boolean closeDirectory, final IndexDeletionPolicy deletionPolicy) throws CorruptIndexException, IOException {
return open(directory, closeDirectory, deletionPolicy, null);
return open(directory, closeDirectory, deletionPolicy, null, false);
}
static DirectoryIndexReader open(final Directory directory, final boolean closeDirectory, final IndexDeletionPolicy deletionPolicy, final IndexCommit commit) throws CorruptIndexException, IOException {
static DirectoryIndexReader open(final Directory directory, final boolean closeDirectory, final IndexDeletionPolicy deletionPolicy, final IndexCommit commit, final boolean readOnly) throws CorruptIndexException, IOException {
SegmentInfos.FindSegmentsFile finder = new SegmentInfos.FindSegmentsFile(directory) {
@ -93,9 +96,11 @@ abstract class DirectoryIndexReader extends IndexReader {
DirectoryIndexReader reader;
if (infos.size() == 1) { // index is optimized
reader = SegmentReader.get(infos, infos.info(0), closeDirectory);
reader = SegmentReader.get(readOnly, infos, infos.info(0), closeDirectory);
} else if (readOnly) {
reader = new ReadOnlyMultiSegmentReader(directory, infos, closeDirectory);
} else {
reader = new MultiSegmentReader(directory, infos, closeDirectory);
reader = new MultiSegmentReader(directory, infos, closeDirectory, false);
}
reader.setDeletionPolicy(deletionPolicy);
return reader;
@ -131,7 +136,7 @@ abstract class DirectoryIndexReader extends IndexReader {
DirectoryIndexReader newReader = doReopen(infos);
if (DirectoryIndexReader.this != newReader) {
newReader.init(directory, infos, closeDirectory);
newReader.init(directory, infos, closeDirectory, readOnly);
newReader.deletionPolicy = deletionPolicy;
}

View File

@ -45,17 +45,33 @@ import java.util.Collection;
opened already, but it cannot be used to delete documents from the index then.
<p>
NOTE: for backwards API compatibility, several methods are not listed
<b>NOTE</b>: for backwards API compatibility, several methods are not listed
as abstract, but have no useful implementations in this base class and
instead always throw UnsupportedOperationException. Subclasses are
strongly encouraged to override these methods, but in many cases may not
need to.
</p>
<p>
<b>NOTE</b>: 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.
</p>
@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 @@ public abstract class IndexReader {
}
}
/** 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. <b>NOTE</b>: 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. <b>NOTE</b>: 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. <b>NOTE</b>: 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}. <b>NOTE</b>: 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}.
* <b>NOTE</b>: 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 @@ public abstract class IndexReader {
* @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}.
* <b>NOTE</b>: 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}. <b>NOTE</b>: 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,11 +293,27 @@ public abstract class IndexReader {
* @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);
}
/**
@ -637,7 +702,7 @@ public abstract class IndexReader {
* 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 @@ public abstract class IndexReader {
* 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 @@ public abstract class IndexReader {
* 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 @@ public abstract class IndexReader {
* @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;

View File

@ -3015,7 +3015,7 @@ public class IndexWriter {
try {
synchronized(this) {
if (segmentInfos.size() == 1){ // add existing index, if any
sReader = SegmentReader.get(segmentInfos.info(0));
sReader = SegmentReader.get(true, segmentInfos.info(0));
merger.add(sReader);
}
}
@ -3974,7 +3974,7 @@ public class IndexWriter {
for (int i = 0; i < numSegments; i++) {
SegmentInfo si = sourceSegmentsClone.info(i);
IndexReader reader = SegmentReader.get(si, MERGE_READ_BUFFER_SIZE, merge.mergeDocStores); // no need to set deleter (yet)
IndexReader reader = SegmentReader.get(true, si, MERGE_READ_BUFFER_SIZE, merge.mergeDocStores); // no need to set deleter (yet)
merger.add(reader);
totDocCount += reader.numDocs();
}

View File

@ -42,8 +42,9 @@ class MultiSegmentReader extends DirectoryIndexReader {
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 @@ class MultiSegmentReader extends DirectoryIndexReader {
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<sis.size();i++) {
@ -70,9 +71,9 @@ class MultiSegmentReader extends DirectoryIndexReader {
}
/** This contructor is only used for {@link #reopen()} */
MultiSegmentReader(Directory directory, SegmentInfos infos, boolean closeDirectory, SegmentReader[] oldReaders, int[] oldStarts, Map oldNormsCache) throws IOException {
super(directory, infos, closeDirectory);
MultiSegmentReader(Directory directory, SegmentInfos infos, boolean closeDirectory, SegmentReader[] oldReaders, int[] oldStarts, Map oldNormsCache, boolean readOnly) throws IOException {
super(directory, infos, closeDirectory, readOnly);
// we put the old SegmentReaders in a map, that allows us
// to lookup a reader using its segment name
Map segmentReaders = new HashMap();
@ -106,7 +107,7 @@ class MultiSegmentReader extends DirectoryIndexReader {
SegmentReader newReader;
if (newReaders[i] == null || infos.info(i).getUseCompoundFile() != newReaders[i].getSegmentInfo().getUseCompoundFile()) {
// this is a new reader; in case we hit an exception we can close it safely
newReader = SegmentReader.get(infos.info(i));
newReader = SegmentReader.get(readOnly, infos.info(i));
} else {
newReader = (SegmentReader) newReaders[i].reopenSegment(infos.info(i));
}
@ -196,11 +197,12 @@ class MultiSegmentReader extends DirectoryIndexReader {
protected synchronized DirectoryIndexReader doReopen(SegmentInfos infos) throws CorruptIndexException, IOException {
if (infos.size() == 1) {
// The index has only one segment now, so we can't refresh the MultiSegmentReader.
// Return a new SegmentReader instead
SegmentReader newReader = SegmentReader.get(infos, infos.info(0), false);
return newReader;
// Return a new [ReadOnly]SegmentReader instead
return SegmentReader.get(readOnly, infos, infos.info(0), false);
} else if (readOnly) {
return new ReadOnlyMultiSegmentReader(directory, infos, closeDirectory, subReaders, starts, normsCache);
} else {
return new MultiSegmentReader(directory, infos, closeDirectory, subReaders, starts, normsCache);
return new MultiSegmentReader(directory, infos, closeDirectory, subReaders, starts, normsCache, false);
}
}
@ -259,7 +261,7 @@ class MultiSegmentReader extends DirectoryIndexReader {
public boolean isDeleted(int n) {
// Don't call ensureOpen() here (it could affect performance)
int i = readerIndex(n); // find segment num
final int i = readerIndex(n); // find segment num
return subReaders[i].isDeleted(n - starts[i]); // dispatch to segment reader
}
@ -287,7 +289,7 @@ class MultiSegmentReader extends DirectoryIndexReader {
return readerIndex(n, this.starts, this.subReaders.length);
}
static int readerIndex(int n, int[] starts, int numSubReaders) { // find reader for doc n:
final static int readerIndex(int n, int[] starts, int numSubReaders) { // find reader for doc n:
int lo = 0; // search starts array
int hi = numSubReaders - 1; // for first element less

View File

@ -0,0 +1,37 @@
package org.apache.lucene.index;
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import org.apache.lucene.store.Directory;
import java.io.IOException;
import java.util.Map;
class ReadOnlyMultiSegmentReader extends MultiSegmentReader {
ReadOnlyMultiSegmentReader(Directory directory, SegmentInfos sis, boolean closeDirectory) throws IOException {
super(directory, sis, closeDirectory, true);
}
ReadOnlyMultiSegmentReader(Directory directory, SegmentInfos infos, boolean closeDirectory, SegmentReader[] oldReaders, int[] oldStarts, Map oldNormsCache) throws IOException {
super(directory, infos, closeDirectory, oldReaders, oldStarts, oldNormsCache, true);
}
protected void acquireWriteLock() {
ReadOnlySegmentReader.noWrite();
}
}

View File

@ -0,0 +1,34 @@
package org.apache.lucene.index;
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class ReadOnlySegmentReader extends SegmentReader {
static void noWrite() {
throw new UnsupportedOperationException("This IndexReader cannot make any changes to the index (it was opened with readOnly = true)");
}
protected void acquireWriteLock() {
noWrite();
}
// Not synchronized
public boolean isDeleted(int n) {
return deletedDocs != null && deletedDocs.get(n);
}
}

View File

@ -61,6 +61,7 @@ class SegmentReader extends DirectoryIndexReader {
private boolean rollbackNormsDirty = false;
private boolean rollbackUndeleteAll = false;
private int rollbackPendingDeleteCount;
private boolean readOnly;
IndexInput freqStream;
IndexInput proxStream;
@ -191,12 +192,38 @@ class SegmentReader extends DirectoryIndexReader {
}
}
private static Class READONLY_IMPL;
static {
try {
String name =
System.getProperty("org.apache.lucene.ReadOnlySegmentReader.class",
ReadOnlySegmentReader.class.getName());
READONLY_IMPL = Class.forName(name);
} catch (ClassNotFoundException e) {
throw new RuntimeException("cannot load ReadOnlySegmentReader class: " + e, e);
} catch (SecurityException se) {
try {
READONLY_IMPL = Class.forName(ReadOnlySegmentReader.class.getName());
} catch (ClassNotFoundException e) {
throw new RuntimeException("cannot load default ReadOnlySegmentReader class: " + e, e);
}
}
}
/**
* @throws CorruptIndexException if the index is corrupt
* @throws IOException if there is a low-level IO error
*/
public static SegmentReader get(SegmentInfo si) throws CorruptIndexException, IOException {
return get(si.dir, si, null, false, false, BufferedIndexInput.BUFFER_SIZE, true);
return get(READ_ONLY_DEFAULT, si.dir, si, null, false, false, BufferedIndexInput.BUFFER_SIZE, true);
}
/**
* @throws CorruptIndexException if the index is corrupt
* @throws IOException if there is a low-level IO error
*/
public static SegmentReader get(boolean readOnly, SegmentInfo si) throws CorruptIndexException, IOException {
return get(readOnly, si.dir, si, null, false, false, BufferedIndexInput.BUFFER_SIZE, true);
}
/**
@ -204,7 +231,7 @@ class SegmentReader extends DirectoryIndexReader {
* @throws IOException if there is a low-level IO error
*/
static SegmentReader get(SegmentInfo si, boolean doOpenStores) throws CorruptIndexException, IOException {
return get(si.dir, si, null, false, false, BufferedIndexInput.BUFFER_SIZE, doOpenStores);
return get(READ_ONLY_DEFAULT, si.dir, si, null, false, false, BufferedIndexInput.BUFFER_SIZE, doOpenStores);
}
/**
@ -212,7 +239,7 @@ class SegmentReader extends DirectoryIndexReader {
* @throws IOException if there is a low-level IO error
*/
public static SegmentReader get(SegmentInfo si, int readBufferSize) throws CorruptIndexException, IOException {
return get(si.dir, si, null, false, false, readBufferSize, true);
return get(READ_ONLY_DEFAULT, si.dir, si, null, false, false, readBufferSize, true);
}
/**
@ -220,16 +247,24 @@ class SegmentReader extends DirectoryIndexReader {
* @throws IOException if there is a low-level IO error
*/
static SegmentReader get(SegmentInfo si, int readBufferSize, boolean doOpenStores) throws CorruptIndexException, IOException {
return get(si.dir, si, null, false, false, readBufferSize, doOpenStores);
return get(READ_ONLY_DEFAULT, si.dir, si, null, false, false, readBufferSize, doOpenStores);
}
/**
* @throws CorruptIndexException if the index is corrupt
* @throws IOException if there is a low-level IO error
*/
public static SegmentReader get(SegmentInfos sis, SegmentInfo si,
static SegmentReader get(boolean readOnly, SegmentInfo si, int readBufferSize, boolean doOpenStores) throws CorruptIndexException, IOException {
return get(readOnly, si.dir, si, null, false, false, readBufferSize, doOpenStores);
}
/**
* @throws CorruptIndexException if the index is corrupt
* @throws IOException if there is a low-level IO error
*/
public static SegmentReader get(boolean readOnly, SegmentInfos sis, SegmentInfo si,
boolean closeDir) throws CorruptIndexException, IOException {
return get(si.dir, si, sis, closeDir, true, BufferedIndexInput.BUFFER_SIZE, true);
return get(readOnly, si.dir, si, sis, closeDir, true, BufferedIndexInput.BUFFER_SIZE, true);
}
/**
@ -241,14 +276,16 @@ class SegmentReader extends DirectoryIndexReader {
boolean closeDir, boolean ownDir,
int readBufferSize)
throws CorruptIndexException, IOException {
return get(dir, si, sis, closeDir, ownDir, readBufferSize, true);
return get(READ_ONLY_DEFAULT, dir, si, sis, closeDir, ownDir, readBufferSize, true);
}
/**
* @throws CorruptIndexException if the index is corrupt
* @throws IOException if there is a low-level IO error
*/
public static SegmentReader get(Directory dir, SegmentInfo si,
public static SegmentReader get(boolean readOnly,
Directory dir,
SegmentInfo si,
SegmentInfos sis,
boolean closeDir, boolean ownDir,
int readBufferSize,
@ -256,11 +293,14 @@ class SegmentReader extends DirectoryIndexReader {
throws CorruptIndexException, IOException {
SegmentReader instance;
try {
instance = (SegmentReader)IMPL.newInstance();
if (readOnly)
instance = (SegmentReader)READONLY_IMPL.newInstance();
else
instance = (SegmentReader)IMPL.newInstance();
} catch (Exception e) {
throw new RuntimeException("cannot load SegmentReader class: " + e, e);
}
instance.init(dir, sis, closeDir);
instance.init(dir, sis, closeDir, readOnly);
instance.initialize(si, readBufferSize, doOpenStores);
return instance;
}
@ -381,10 +421,13 @@ class SegmentReader extends DirectoryIndexReader {
} else {
// segment not referenced anymore, reopen not possible
// or segment format changed
newReader = SegmentReader.get(infos, infos.info(0), false);
newReader = SegmentReader.get(readOnly, infos, infos.info(0), false);
}
} else {
return new MultiSegmentReader(directory, infos, closeDirectory, new SegmentReader[] {this}, null, null);
if (readOnly)
return new ReadOnlyMultiSegmentReader(directory, infos, closeDirectory, new SegmentReader[] {this}, null, null);
else
return new MultiSegmentReader(directory, infos, closeDirectory, new SegmentReader[] {this}, null, null, false);
}
return newReader;
@ -412,9 +455,15 @@ class SegmentReader extends DirectoryIndexReader {
// clone reader
SegmentReader clone = new SegmentReader();
SegmentReader clone;
if (readOnly)
clone = new ReadOnlySegmentReader();
else
clone = new SegmentReader();
boolean success = false;
try {
clone.readOnly = readOnly;
clone.directory = directory;
clone.si = si;
clone.segment = segment;

View File

@ -1329,4 +1329,61 @@ public class TestIndexReader extends LuceneTestCase
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();
}
}