mirror of https://github.com/apache/lucene.git
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:
parent
59b8dca69a
commit
f0d2a151ca
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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,8 +71,8 @@ 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
|
||||
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue