You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by rm...@apache.org on 2015/06/04 20:20:44 UTC
svn commit: r1683606 [1/2] - in /lucene/dev/trunk: lucene/
lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/
lucene/core/src/java/org/apache/lucene/codecs/lucene50/
lucene/core/src/java/org/apache/lucene/index/
lucene/core/src/java/org/apache...
Author: rmuir
Date: Thu Jun 4 18:20:44 2015
New Revision: 1683606
URL: http://svn.apache.org/r1683606
Log:
LUCENE-6508: Simplify directory/lock API
Added:
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/SleepingLockWrapper.java (with props)
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockValidatingDirectoryWrapper.java (with props)
lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestSleepingLockWrapper.java (with props)
lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/store/TestNativeFSLockFactory.java (with props)
lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/store/TestSimpleFSLockFactory.java (with props)
lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/store/TestSingleInstanceLockFactory.java (with props)
lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/store/BaseLockFactoryTestCase.java (with props)
Removed:
lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/store/TestLock.java
Modified:
lucene/dev/trunk/lucene/CHANGES.txt
lucene/dev/trunk/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextCompoundFormat.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50CompoundReader.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/CheckIndex.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/DocumentsWriter.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/DocumentsWriterPerThread.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/IndexFileDeleter.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/IndexWriterConfig.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/SegmentInfos.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/BaseDirectory.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/Directory.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/FSLockFactory.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/FileSwitchDirectory.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/FilterDirectory.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/Lock.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockFactory.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockObtainFailedException.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockStressTest.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/NativeFSLockFactory.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/NoLockFactory.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/SimpleFSLockFactory.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/SingleInstanceLockFactory.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/VerifyingLockFactory.java
lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestAddIndexes.java
lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriter.java
lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterExceptions.java
lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/store/TestDirectory.java
lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/store/TestLockFactory.java
lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/DirectoryTaxonomyWriter.java
lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/index/BaseCompoundFormatTestCase.java
lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/store/MockDirectoryWrapper.java
lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/util/TestUtil.java
lucene/dev/trunk/lucene/test-framework/src/test/org/apache/lucene/store/TestMockDirectoryWrapper.java
lucene/dev/trunk/solr/CHANGES.txt
lucene/dev/trunk/solr/contrib/morphlines-core/src/test-files/solr/collection1/conf/solrconfig.xml
lucene/dev/trunk/solr/contrib/morphlines-core/src/test-files/solr/minimr/conf/solrconfig.xml
lucene/dev/trunk/solr/contrib/morphlines-core/src/test-files/solr/mrunit/conf/solrconfig.xml
lucene/dev/trunk/solr/contrib/morphlines-core/src/test-files/solr/solrcelltest/collection1/conf/solrconfig.xml
lucene/dev/trunk/solr/contrib/morphlines-core/src/test-files/solr/solrcloud/conf/solrconfig.xml
lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/SolrConfig.java
lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/SolrCore.java
lucene/dev/trunk/solr/core/src/java/org/apache/solr/store/hdfs/HdfsLockFactory.java
lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/bad-solrconfig-multiple-indexconfigs.xml
lucene/dev/trunk/solr/core/src/test/org/apache/solr/store/hdfs/HdfsLockFactoryTest.java
lucene/dev/trunk/solr/example/example-DIH/solr/db/conf/solrconfig.xml
lucene/dev/trunk/solr/example/example-DIH/solr/mail/conf/solrconfig.xml
lucene/dev/trunk/solr/example/example-DIH/solr/rss/conf/solrconfig.xml
lucene/dev/trunk/solr/example/example-DIH/solr/solr/conf/solrconfig.xml
lucene/dev/trunk/solr/example/example-DIH/solr/tika/conf/solrconfig.xml
lucene/dev/trunk/solr/example/files/conf/solrconfig.xml
lucene/dev/trunk/solr/server/solr/configsets/data_driven_schema_configs/conf/solrconfig.xml
lucene/dev/trunk/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml
Modified: lucene/dev/trunk/lucene/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/CHANGES.txt?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/CHANGES.txt (original)
+++ lucene/dev/trunk/lucene/CHANGES.txt Thu Jun 4 18:20:44 2015
@@ -54,6 +54,14 @@ New Features
and queries, for fast "bbox/polygon contains lat/lon points" (Mike
McCandless)
+API Changes
+
+* LUCENE-6508: Simplify Lock api, there is now just
+ Directory.obtainLock() which returns a Lock that can be
+ released (or fails with exception). Add lock verification
+ to IndexWriter. Improve exception messages when locking fails.
+ (Uwe Schindler, Mike McCandless, Robert Muir)
+
Bug fixes
* LUCENE-6500: ParallelCompositeReader did not always call
Modified: lucene/dev/trunk/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextCompoundFormat.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextCompoundFormat.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextCompoundFormat.java (original)
+++ lucene/dev/trunk/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextCompoundFormat.java Thu Jun 4 18:20:44 2015
@@ -151,7 +151,7 @@ public class SimpleTextCompoundFormat ex
public void renameFile(String source, String dest) { throw new UnsupportedOperationException(); }
@Override
- public Lock makeLock(String name) { throw new UnsupportedOperationException(); }
+ public Lock obtainLock(String name) { throw new UnsupportedOperationException(); }
};
}
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50CompoundReader.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50CompoundReader.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50CompoundReader.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/codecs/lucene50/Lucene50CompoundReader.java Thu Jun 4 18:20:44 2015
@@ -179,7 +179,7 @@ final class Lucene50CompoundReader exten
}
@Override
- public Lock makeLock(String name) {
+ public Lock obtainLock(String name) {
throw new UnsupportedOperationException();
}
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/CheckIndex.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/CheckIndex.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/CheckIndex.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/CheckIndex.java Thu Jun 4 18:20:44 2015
@@ -47,7 +47,6 @@ import org.apache.lucene.store.FSDirecto
import org.apache.lucene.store.IOContext;
import org.apache.lucene.store.IndexInput;
import org.apache.lucene.store.Lock;
-import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.util.Accountables;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef;
@@ -356,7 +355,7 @@ public class CheckIndex implements Close
/** Create a new CheckIndex on the directory. */
public CheckIndex(Directory dir) throws IOException {
- this(dir, dir.makeLock(IndexWriter.WRITE_LOCK_NAME));
+ this(dir, dir.obtainLock(IndexWriter.WRITE_LOCK_NAME));
}
/**
@@ -370,9 +369,6 @@ public class CheckIndex implements Close
this.dir = dir;
this.writeLock = writeLock;
this.infoStream = null;
- if (!writeLock.obtain(IndexWriterConfig.WRITE_LOCK_TIMEOUT)) { // obtain write lock
- throw new LockObtainFailedException("Index locked for write: " + writeLock);
- }
}
private void ensureOpen() {
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/DocumentsWriter.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/DocumentsWriter.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/DocumentsWriter.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/DocumentsWriter.java Thu Jun 4 18:20:44 2015
@@ -95,6 +95,7 @@ import org.apache.lucene.util.InfoStream
*/
final class DocumentsWriter implements Closeable, Accountable {
+ private final Directory directoryOrig; // no wrapping, for infos
private final Directory directory;
private volatile boolean closed;
@@ -123,7 +124,8 @@ final class DocumentsWriter implements C
private final Queue<Event> events;
- DocumentsWriter(IndexWriter writer, LiveIndexWriterConfig config, Directory directory) {
+ DocumentsWriter(IndexWriter writer, LiveIndexWriterConfig config, Directory directoryOrig, Directory directory) {
+ this.directoryOrig = directoryOrig;
this.directory = directory;
this.config = config;
this.infoStream = config.getInfoStream();
@@ -393,7 +395,7 @@ final class DocumentsWriter implements C
if (state.isActive() && state.dwpt == null) {
final FieldInfos.Builder infos = new FieldInfos.Builder(
writer.globalFieldNumberMap);
- state.dwpt = new DocumentsWriterPerThread(writer.newSegmentName(),
+ state.dwpt = new DocumentsWriterPerThread(writer.newSegmentName(), directoryOrig,
directory, config, infoStream, deleteQueue, infos,
writer.pendingNumDocs, writer.enableTestPoints);
}
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/DocumentsWriterPerThread.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/DocumentsWriterPerThread.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/DocumentsWriterPerThread.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/DocumentsWriterPerThread.java Thu Jun 4 18:20:44 2015
@@ -158,9 +158,9 @@ class DocumentsWriterPerThread {
private final LiveIndexWriterConfig indexWriterConfig;
private final boolean enableTestPoints;
- public DocumentsWriterPerThread(String segmentName, Directory directory, LiveIndexWriterConfig indexWriterConfig, InfoStream infoStream, DocumentsWriterDeleteQueue deleteQueue,
+ public DocumentsWriterPerThread(String segmentName, Directory directoryOrig, Directory directory, LiveIndexWriterConfig indexWriterConfig, InfoStream infoStream, DocumentsWriterDeleteQueue deleteQueue,
FieldInfos.Builder fieldInfos, AtomicLong pendingNumDocs, boolean enableTestPoints) throws IOException {
- this.directoryOrig = directory;
+ this.directoryOrig = directoryOrig;
this.directory = new TrackingDirectoryWrapper(directory);
this.fieldInfos = fieldInfos;
this.indexWriterConfig = indexWriterConfig;
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/IndexFileDeleter.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/IndexFileDeleter.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/IndexFileDeleter.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/IndexFileDeleter.java Thu Jun 4 18:20:44 2015
@@ -102,8 +102,9 @@ final class IndexFileDeleter implements
private List<CommitPoint> commitsToDelete = new ArrayList<>();
private final InfoStream infoStream;
- private Directory directory;
- private IndexDeletionPolicy policy;
+ private final Directory directoryOrig; // for commit point metadata
+ private final Directory directory;
+ private final IndexDeletionPolicy policy;
final boolean startingCommitDeleted;
private SegmentInfos lastSegmentInfos;
@@ -126,7 +127,7 @@ final class IndexFileDeleter implements
* any files not referenced by any of the commits.
* @throws IOException if there is a low-level IO error
*/
- public IndexFileDeleter(Directory directory, IndexDeletionPolicy policy, SegmentInfos segmentInfos,
+ public IndexFileDeleter(Directory directoryOrig, Directory directory, IndexDeletionPolicy policy, SegmentInfos segmentInfos,
InfoStream infoStream, IndexWriter writer, boolean initialIndexExists) throws IOException {
Objects.requireNonNull(writer);
this.infoStream = infoStream;
@@ -139,6 +140,7 @@ final class IndexFileDeleter implements
}
this.policy = policy;
+ this.directoryOrig = directoryOrig;
this.directory = directory;
// First pass: walk the files and initialize our ref
@@ -165,7 +167,7 @@ final class IndexFileDeleter implements
}
SegmentInfos sis = null;
try {
- sis = SegmentInfos.readCommit(directory, fileName);
+ sis = SegmentInfos.readCommit(directoryOrig, fileName);
} catch (FileNotFoundException | NoSuchFileException e) {
// LUCENE-948: on NFS (and maybe others), if
// you have writers switching back and forth
@@ -179,7 +181,7 @@ final class IndexFileDeleter implements
}
}
if (sis != null) {
- final CommitPoint commitPoint = new CommitPoint(commitsToDelete, directory, sis);
+ final CommitPoint commitPoint = new CommitPoint(commitsToDelete, directoryOrig, sis);
if (sis.getGeneration() == segmentInfos.getGeneration()) {
currentCommitPoint = commitPoint;
}
@@ -205,14 +207,14 @@ final class IndexFileDeleter implements
// try now to explicitly open this commit point:
SegmentInfos sis = null;
try {
- sis = SegmentInfos.readCommit(directory, currentSegmentsFile);
+ sis = SegmentInfos.readCommit(directoryOrig, currentSegmentsFile);
} catch (IOException e) {
throw new CorruptIndexException("unable to read current segments_N file", currentSegmentsFile, e);
}
if (infoStream.isEnabled("IFD")) {
infoStream.message("IFD", "forced open of current segments file " + segmentInfos.getSegmentsFileName());
}
- currentCommitPoint = new CommitPoint(commitsToDelete, directory, sis);
+ currentCommitPoint = new CommitPoint(commitsToDelete, directoryOrig, sis);
commits.add(currentCommitPoint);
incRef(sis, true);
}
@@ -557,7 +559,7 @@ final class IndexFileDeleter implements
if (isCommit) {
// Append to our commits list:
- commits.add(new CommitPoint(commitsToDelete, directory, segmentInfos));
+ commits.add(new CommitPoint(commitsToDelete, directoryOrig, segmentInfos));
// Tell policy so it can remove commits:
policy.onCommit(commits);
@@ -780,14 +782,14 @@ final class IndexFileDeleter implements
Collection<String> files;
String segmentsFileName;
boolean deleted;
- Directory directory;
+ Directory directoryOrig;
Collection<CommitPoint> commitsToDelete;
long generation;
final Map<String,String> userData;
private final int segmentCount;
- public CommitPoint(Collection<CommitPoint> commitsToDelete, Directory directory, SegmentInfos segmentInfos) throws IOException {
- this.directory = directory;
+ public CommitPoint(Collection<CommitPoint> commitsToDelete, Directory directoryOrig, SegmentInfos segmentInfos) throws IOException {
+ this.directoryOrig = directoryOrig;
this.commitsToDelete = commitsToDelete;
userData = segmentInfos.getUserData();
segmentsFileName = segmentInfos.getSegmentsFileName();
@@ -818,7 +820,7 @@ final class IndexFileDeleter implements
@Override
public Directory getDirectory() {
- return directory;
+ return directoryOrig;
}
@Override
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java Thu Jun 4 18:20:44 2015
@@ -60,6 +60,7 @@ import org.apache.lucene.store.LockObtai
import org.apache.lucene.store.MergeInfo;
import org.apache.lucene.store.RateLimitedIndexOutput;
import org.apache.lucene.store.TrackingDirectoryWrapper;
+import org.apache.lucene.store.LockValidatingDirectoryWrapper;
import org.apache.lucene.util.Accountable;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef;
@@ -118,9 +119,7 @@ import org.apache.lucene.util.Version;
<p>Opening an <code>IndexWriter</code> creates a lock file for the directory in use. Trying to open
another <code>IndexWriter</code> on the same directory will lead to a
- {@link LockObtainFailedException}. The {@link LockObtainFailedException}
- is also thrown if an IndexReader on the same directory is used to delete documents
- from the index.</p>
+ {@link LockObtainFailedException}.</p>
<a name="deletionPolicy"></a>
<p>Expert: <code>IndexWriter</code> allows an optional
@@ -254,8 +253,9 @@ public class IndexWriter implements Clos
// when unrecoverable disaster strikes, we populate this with the reason that we had to close IndexWriter
volatile Throwable tragedy;
- private final Directory directory; // where this index resides
- private final Directory mergeDirectory; // used for merging
+ private final Directory directoryOrig; // original user directory
+ private final Directory directory; // wrapped with additional checks
+ private final Directory mergeDirectory; // wrapped with throttling: used for merging
private final Analyzer analyzer; // how to analyze text
private final AtomicLong changeCount = new AtomicLong(); // increments every time a change is completed
@@ -645,7 +645,7 @@ public class IndexWriter implements Clos
// Make sure no new readers can be opened if another thread just closed us:
ensureOpen(false);
- assert info.info.dir == directory: "info.dir=" + info.info.dir + " vs " + directory;
+ assert info.info.dir == directoryOrig: "info.dir=" + info.info.dir + " vs " + directoryOrig;
ReadersAndUpdates rld = readerMap.get(info);
if (rld == null) {
@@ -754,29 +754,37 @@ public class IndexWriter implements Clos
public IndexWriter(Directory d, IndexWriterConfig conf) throws IOException {
conf.setIndexWriter(this); // prevent reuse by other instances
config = conf;
-
- directory = d;
-
- // Directory we use for merging, so we can abort running merges, and so
- // merge schedulers can optionally rate-limit per-merge IO:
- mergeDirectory = addMergeRateLimiters(d);
-
- analyzer = config.getAnalyzer();
infoStream = config.getInfoStream();
- mergeScheduler = config.getMergeScheduler();
- mergeScheduler.setInfoStream(infoStream);
- codec = config.getCodec();
-
- bufferedUpdatesStream = new BufferedUpdatesStream(infoStream);
- poolReaders = config.getReaderPooling();
+
+ // obtain the write.lock. If the user configured a timeout,
+ // we wrap with a sleeper and this might take some time.
+ long timeout = config.getWriteLockTimeout();
+ final Directory lockDir;
+ if (timeout == 0) {
+ // user doesn't want sleep/retries
+ lockDir = d;
+ } else {
+ lockDir = new SleepingLockWrapper(d, timeout);
+ }
+ writeLock = lockDir.obtainLock(WRITE_LOCK_NAME);
+
+ boolean success = false;
+ try {
+ directoryOrig = d;
+ directory = new LockValidatingDirectoryWrapper(d, writeLock);
- writeLock = directory.makeLock(WRITE_LOCK_NAME);
+ // Directory we use for merging, so we can abort running merges, and so
+ // merge schedulers can optionally rate-limit per-merge IO:
+ mergeDirectory = addMergeRateLimiters(directory);
+
+ analyzer = config.getAnalyzer();
+ mergeScheduler = config.getMergeScheduler();
+ mergeScheduler.setInfoStream(infoStream);
+ codec = config.getCodec();
- if (!writeLock.obtain(config.getWriteLockTimeout())) // obtain write lock
- throw new LockObtainFailedException("Index locked for write: " + writeLock);
+ bufferedUpdatesStream = new BufferedUpdatesStream(infoStream);
+ poolReaders = config.getReaderPooling();
- boolean success = false;
- try {
OpenMode mode = config.getOpenMode();
boolean create;
if (mode == OpenMode.CREATE) {
@@ -822,7 +830,7 @@ public class IndexWriter implements Clos
// Do not use SegmentInfos.read(Directory) since the spooky
// retrying it does is not necessary here (we hold the write lock):
- segmentInfos = SegmentInfos.readCommit(directory, lastSegmentsFile);
+ segmentInfos = SegmentInfos.readCommit(directoryOrig, lastSegmentsFile);
IndexCommit commit = config.getIndexCommit();
if (commit != null) {
@@ -831,9 +839,9 @@ public class IndexWriter implements Clos
// preserve write-once. This is important if
// readers are open against the future commit
// points.
- if (commit.getDirectory() != directory)
- throw new IllegalArgumentException("IndexCommit's directory doesn't match my directory");
- SegmentInfos oldInfos = SegmentInfos.readCommit(directory, commit.getSegmentsFileName());
+ if (commit.getDirectory() != directoryOrig)
+ throw new IllegalArgumentException("IndexCommit's directory doesn't match my directory, expected=" + directoryOrig + ", got=" + commit.getDirectory());
+ SegmentInfos oldInfos = SegmentInfos.readCommit(directoryOrig, commit.getSegmentsFileName());
segmentInfos.replace(oldInfos);
changed();
if (infoStream.isEnabled("IW")) {
@@ -848,13 +856,13 @@ public class IndexWriter implements Clos
// start with previous field numbers, but new FieldInfos
globalFieldNumberMap = getFieldNumberMap();
config.getFlushPolicy().init(config);
- docWriter = new DocumentsWriter(this, config, directory);
+ docWriter = new DocumentsWriter(this, config, directoryOrig, directory);
eventQueue = docWriter.eventQueue();
// Default deleter (for backwards compatibility) is
// KeepOnlyLastCommitDeleter:
synchronized(this) {
- deleter = new IndexFileDeleter(directory,
+ deleter = new IndexFileDeleter(directoryOrig, directory,
config.getIndexDeletionPolicy(),
segmentInfos, infoStream, this,
initialIndexExists);
@@ -937,7 +945,7 @@ public class IndexWriter implements Clos
private void messageState() {
if (infoStream.isEnabled("IW") && didMessageState == false) {
didMessageState = true;
- infoStream.message("IW", "\ndir=" + directory + "\n" +
+ infoStream.message("IW", "\ndir=" + directoryOrig + "\n" +
"index=" + segString() + "\n" +
"version=" + Version.LATEST.toString() + "\n" +
config.toString());
@@ -1036,7 +1044,8 @@ public class IndexWriter implements Clos
/** Returns the Directory used by this index. */
public Directory getDirectory() {
- return directory;
+ // return the original directory the user supplied, unwrapped.
+ return directoryOrig;
}
/** Returns the analyzer used by this index. */
@@ -2274,7 +2283,7 @@ public class IndexWriter implements Clos
for(int i=0;i<dirs.length;i++) {
if (dups.contains(dirs[i]))
throw new IllegalArgumentException("Directory " + dirs[i] + " appears more than once");
- if (dirs[i] == directory)
+ if (dirs[i] == directoryOrig)
throw new IllegalArgumentException("Cannot add directory to itself");
dups.add(dirs[i]);
}
@@ -2288,13 +2297,13 @@ public class IndexWriter implements Clos
for(int i=0;i<dirs.length;i++) {
boolean success = false;
try {
- Lock lock = dirs[i].makeLock(WRITE_LOCK_NAME);
+ Lock lock = dirs[i].obtainLock(WRITE_LOCK_NAME);
locks.add(lock);
- lock.obtain(config.getWriteLockTimeout());
success = true;
} finally {
if (success == false) {
// Release all previously acquired locks:
+ // TODO: addSuppressed? it could be many...
IOUtils.closeWhileHandlingException(locks);
}
}
@@ -2334,8 +2343,6 @@ public class IndexWriter implements Clos
*
* @throws CorruptIndexException if the index is corrupt
* @throws IOException if there is a low-level IO error
- * @throws LockObtainFailedException if we were unable to
- * acquire the write lock in at least one directory
* @throws IllegalArgumentException if addIndexes would cause
* the index to exceed {@link #MAX_DOCS}
*/
@@ -2496,7 +2503,7 @@ public class IndexWriter implements Clos
// abortable so that IW.close(false) is able to stop it
TrackingDirectoryWrapper trackingDir = new TrackingDirectoryWrapper(directory);
- SegmentInfo info = new SegmentInfo(directory, Version.LATEST, mergedName, -1,
+ SegmentInfo info = new SegmentInfo(directoryOrig, Version.LATEST, mergedName, -1,
false, codec, Collections.emptyMap(), StringHelper.randomId(), new HashMap<>());
SegmentMerger merger = new SegmentMerger(Arrays.asList(readers), info, infoStream, trackingDir,
@@ -2600,7 +2607,7 @@ public class IndexWriter implements Clos
//System.out.println("copy seg=" + info.info.name + " version=" + info.info.getVersion());
// Same SI as before but we change directory and name
- SegmentInfo newInfo = new SegmentInfo(directory, info.info.getVersion(), segName, info.info.maxDoc(),
+ SegmentInfo newInfo = new SegmentInfo(directoryOrig, info.info.getVersion(), segName, info.info.maxDoc(),
info.info.getUseCompoundFile(), info.info.getCodec(),
info.info.getDiagnostics(), info.info.getId(), info.info.getAttributes());
SegmentCommitInfo newInfoPerCommit = new SegmentCommitInfo(newInfo, info.getDelCount(), info.getDelGen(),
@@ -3075,7 +3082,7 @@ public class IndexWriter implements Clos
private synchronized void ensureValidMerge(MergePolicy.OneMerge merge) {
for(SegmentCommitInfo info : merge.segments) {
if (!segmentInfos.contains(info)) {
- throw new MergePolicy.MergeException("MergePolicy selected a segment (" + info.info.name + ") that is not in the current index " + segString(), directory);
+ throw new MergePolicy.MergeException("MergePolicy selected a segment (" + info.info.name + ") that is not in the current index " + segString(), directoryOrig);
}
}
}
@@ -3599,7 +3606,7 @@ public class IndexWriter implements Clos
}
return false;
}
- if (info.info.dir != directory) {
+ if (info.info.dir != directoryOrig) {
isExternal = true;
}
if (segmentsToMerge.containsKey(info)) {
@@ -3732,7 +3739,7 @@ public class IndexWriter implements Clos
// ConcurrentMergePolicy we keep deterministic segment
// names.
final String mergeSegmentName = newSegmentName();
- SegmentInfo si = new SegmentInfo(directory, Version.LATEST, mergeSegmentName, -1, false, codec, Collections.emptyMap(), StringHelper.randomId(), new HashMap<>());
+ SegmentInfo si = new SegmentInfo(directoryOrig, Version.LATEST, mergeSegmentName, -1, false, codec, Collections.emptyMap(), StringHelper.randomId(), new HashMap<>());
Map<String,String> details = new HashMap<>();
details.put("mergeMaxNumSegments", "" + merge.maxNumSegments);
details.put("mergeFactor", Integer.toString(merge.segments.size()));
@@ -4365,9 +4372,17 @@ public class IndexWriter implements Clos
* currently locked.
* @param directory the directory to check for a lock
* @throws IOException if there is a low-level IO error
+ * @deprecated Use of this method can only lead to race conditions. Try
+ * to actually obtain a lock instead.
*/
+ @Deprecated
public static boolean isLocked(Directory directory) throws IOException {
- return directory.makeLock(WRITE_LOCK_NAME).isLocked();
+ try {
+ directory.obtainLock(WRITE_LOCK_NAME).close();
+ return false;
+ } catch (LockObtainFailedException failed) {
+ return true;
+ }
}
/** If {@link DirectoryReader#open(IndexWriter,boolean)} has
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/IndexWriterConfig.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/IndexWriterConfig.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/IndexWriterConfig.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/IndexWriterConfig.java Thu Jun 4 18:20:44 2015
@@ -265,7 +265,8 @@ public final class IndexWriterConfig ext
/**
* Sets the maximum time to wait for a write lock (in milliseconds) for this
* instance. You can change the default value for all instances by calling
- * {@link #setDefaultWriteLockTimeout(long)}.
+ * {@link #setDefaultWriteLockTimeout(long)}. Note that the value can be zero,
+ * for no sleep/retry behavior.
*
* <p>Only takes effect when IndexWriter is first created. */
public IndexWriterConfig setWriteLockTimeout(long writeLockTimeout) {
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/SegmentInfos.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/SegmentInfos.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/SegmentInfos.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/SegmentInfos.java Thu Jun 4 18:20:44 2015
@@ -441,7 +441,6 @@ public final class SegmentInfos implemen
segnOutput.writeInt(e.getKey());
segnOutput.writeSetOfStrings(e.getValue());
}
- assert si.dir == directory;
}
segnOutput.writeMapOfStrings(userData);
CodecUtil.writeFooter(segnOutput);
Added: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/SleepingLockWrapper.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/SleepingLockWrapper.java?rev=1683606&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/SleepingLockWrapper.java (added)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/SleepingLockWrapper.java Thu Jun 4 18:20:44 2015
@@ -0,0 +1,113 @@
+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 java.io.IOException;
+
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.FilterDirectory;
+import org.apache.lucene.store.Lock;
+import org.apache.lucene.store.LockObtainFailedException;
+import org.apache.lucene.util.ThreadInterruptedException;
+
+/**
+ * Directory that wraps another, and that sleeps and retries
+ * if obtaining the lock fails.
+ * <p>
+ * This is not a good idea.
+ */
+final class SleepingLockWrapper extends FilterDirectory {
+
+ /**
+ * Pass this lockWaitTimeout to try forever to obtain the lock.
+ */
+ public static final long LOCK_OBTAIN_WAIT_FOREVER = -1;
+
+ /**
+ * How long {@link #obtainLock} waits, in milliseconds,
+ * in between attempts to acquire the lock.
+ */
+ public static long DEFAULT_POLL_INTERVAL = 1000;
+
+ private final long lockWaitTimeout;
+ private final long pollInterval;
+
+ /**
+ * Create a new SleepingLockFactory
+ * @param delegate underlying directory to wrap
+ * @param lockWaitTimeout length of time to wait in milliseconds
+ * or {@link #LOCK_OBTAIN_WAIT_FOREVER} to retry forever.
+ */
+ public SleepingLockWrapper(Directory delegate, long lockWaitTimeout) {
+ this(delegate, lockWaitTimeout, DEFAULT_POLL_INTERVAL);
+ }
+
+ /**
+ * Create a new SleepingLockFactory
+ * @param delegate underlying directory to wrap
+ * @param lockWaitTimeout length of time to wait in milliseconds
+ * or {@link #LOCK_OBTAIN_WAIT_FOREVER} to retry forever.
+ * @param pollInterval poll once per this interval in milliseconds until
+ * {@code lockWaitTimeout} is exceeded.
+ */
+ public SleepingLockWrapper(Directory delegate, long lockWaitTimeout, long pollInterval) {
+ super(delegate);
+ this.lockWaitTimeout = lockWaitTimeout;
+ this.pollInterval = pollInterval;
+ if (lockWaitTimeout < 0 && lockWaitTimeout != LOCK_OBTAIN_WAIT_FOREVER) {
+ throw new IllegalArgumentException("lockWaitTimeout should be LOCK_OBTAIN_WAIT_FOREVER or a non-negative number (got " + lockWaitTimeout + ")");
+ }
+ if (pollInterval < 0) {
+ throw new IllegalArgumentException("pollInterval must be a non-negative number (got " + pollInterval + ")");
+ }
+ }
+
+ @Override
+ public Lock obtainLock(String lockName) throws IOException {
+ LockObtainFailedException failureReason = null;
+ long maxSleepCount = lockWaitTimeout / pollInterval;
+ long sleepCount = 0;
+
+ do {
+ try {
+ return in.obtainLock(lockName);
+ } catch (LockObtainFailedException failed) {
+ if (failureReason == null) {
+ failureReason = failed;
+ }
+ }
+ try {
+ Thread.sleep(pollInterval);
+ } catch (InterruptedException ie) {
+ throw new ThreadInterruptedException(ie);
+ }
+ } while (sleepCount++ < maxSleepCount || lockWaitTimeout == LOCK_OBTAIN_WAIT_FOREVER);
+
+ // we failed to obtain the lock in the required time
+ String reason = "Lock obtain timed out: " + this.toString();
+ if (failureReason != null) {
+ reason += ": " + failureReason;
+ }
+ throw new LockObtainFailedException(reason, failureReason);
+ }
+
+ @Override
+ public String toString() {
+ return "SleepingLockWrapper(" + in + ")";
+ }
+}
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/BaseDirectory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/BaseDirectory.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/BaseDirectory.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/BaseDirectory.java Thu Jun 4 18:20:44 2015
@@ -17,6 +17,7 @@ package org.apache.lucene.store;
* limitations under the License.
*/
+import java.io.IOException;
/**
* Base implementation for a concrete {@link Directory} that uses a {@link LockFactory} for locking.
@@ -40,8 +41,8 @@ public abstract class BaseDirectory exte
}
@Override
- public final Lock makeLock(String name) {
- return lockFactory.makeLock(this, name);
+ public final Lock obtainLock(String name) throws IOException {
+ return lockFactory.obtainLock(this, name);
}
@Override
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/Directory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/Directory.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/Directory.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/Directory.java Thu Jun 4 18:20:44 2015
@@ -50,8 +50,7 @@ public abstract class Directory implemen
public abstract String[] listAll() throws IOException;
/** Removes an existing file in the directory. */
- public abstract void deleteFile(String name)
- throws IOException;
+ public abstract void deleteFile(String name) throws IOException;
/**
* Returns the length of a file in the directory. This method follows the
@@ -110,10 +109,14 @@ public abstract class Directory implemen
return new BufferedChecksumIndexInput(openInput(name, context));
}
- /** Construct a {@link Lock}.
+ /**
+ * Returns an obtained {@link Lock}.
* @param name the name of the lock file
+ * @throws LockObtainFailedException (optional specific exception) if the lock could
+ * not be obtained because it is currently held elsewhere.
+ * @throws IOException if any i/o error occurs attempting to gain the lock
*/
- public abstract Lock makeLock(String name);
+ public abstract Lock obtainLock(String name) throws IOException;
/** Closes the store. */
@Override
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/FSLockFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/FSLockFactory.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/FSLockFactory.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/FSLockFactory.java Thu Jun 4 18:20:44 2015
@@ -17,6 +17,8 @@ package org.apache.lucene.store;
* limitations under the License.
*/
+import java.io.IOException;
+
/**
* Base class for file system based locking implementation.
* This class is explicitly checking that the passed {@link Directory}
@@ -32,14 +34,17 @@ public abstract class FSLockFactory exte
}
@Override
- public final Lock makeLock(Directory dir, String lockName) {
+ public final Lock obtainLock(Directory dir, String lockName) throws IOException {
if (!(dir instanceof FSDirectory)) {
throw new UnsupportedOperationException(getClass().getSimpleName() + " can only be used with FSDirectory subclasses, got: " + dir);
}
- return makeFSLock((FSDirectory) dir, lockName);
+ return obtainFSLock((FSDirectory) dir, lockName);
}
- /** Implement this method to create a lock for a FSDirectory instance. */
- protected abstract Lock makeFSLock(FSDirectory dir, String lockName);
+ /**
+ * Implement this method to obtain a lock for a FSDirectory instance.
+ * @throws IOException if the lock could not be obtained.
+ */
+ protected abstract Lock obtainFSLock(FSDirectory dir, String lockName) throws IOException;
}
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/FileSwitchDirectory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/FileSwitchDirectory.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/FileSwitchDirectory.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/FileSwitchDirectory.java Thu Jun 4 18:20:44 2015
@@ -71,8 +71,8 @@ public class FileSwitchDirectory extends
}
@Override
- public Lock makeLock(String name) {
- return getDirectory(name).makeLock(name);
+ public Lock obtainLock(String name) throws IOException {
+ return getDirectory(name).obtainLock(name);
}
@Override
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/FilterDirectory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/FilterDirectory.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/FilterDirectory.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/FilterDirectory.java Thu Jun 4 18:20:44 2015
@@ -90,8 +90,8 @@ public class FilterDirectory extends Dir
}
@Override
- public Lock makeLock(String name) {
- return in.makeLock(name);
+ public Lock obtainLock(String name) throws IOException {
+ return in.obtainLock(name);
}
@Override
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/Lock.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/Lock.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/Lock.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/Lock.java Thu Jun 4 18:20:44 2015
@@ -20,126 +20,39 @@ package org.apache.lucene.store;
import java.io.Closeable;
import java.io.IOException;
-import org.apache.lucene.util.ThreadInterruptedException;
-
/** An interprocess mutex lock.
* <p>Typical use might look like:<pre class="prettyprint">
- * new Lock.With(directory.makeLock("my.lock")) {
- * public Object doBody() {
- * <i>... code to execute while locked ...</i>
- * }
- * }.run();
+ * try (final Lock lock = directory.obtainLock("my.lock")) {
+ * // ... code to execute while locked ...
+ * }
* </pre>
*
- * @see Directory#makeLock(String)
+ * @see Directory#obtainLock(String)
*
* @lucene.internal
*/
public abstract class Lock implements Closeable {
- /** How long {@link #obtain(long)} waits, in milliseconds,
- * in between attempts to acquire the lock. */
- public static long LOCK_POLL_INTERVAL = 1000;
-
- /** Pass this value to {@link #obtain(long)} to try
- * forever to obtain the lock. */
- public static final long LOCK_OBTAIN_WAIT_FOREVER = -1;
-
- /** Attempts to obtain exclusive access and immediately return
- * upon success or failure. Use {@link #close} to
- * release the lock.
- * @return true iff exclusive access is obtained
- */
- public abstract boolean obtain() throws IOException;
-
- /**
- * If a lock obtain called, this failureReason may be set
- * with the "root cause" Exception as to why the lock was
- * not obtained.
+ /**
+ * Releases exclusive access.
+ * <p>
+ * Note that exceptions thrown from close may require
+ * human intervention, as it may mean the lock was no
+ * longer valid, or that fs permissions prevent removal
+ * of the lock file, or other reasons.
+ * <p>
+ * {@inheritDoc}
+ * @throws LockReleaseFailedException optional specific exception) if
+ * the lock could not be properly released.
*/
- protected Throwable failureReason;
-
- /** Attempts to obtain an exclusive lock within amount of
- * time given. Polls once per {@link #LOCK_POLL_INTERVAL}
- * (currently 1000) milliseconds until lockWaitTimeout is
- * passed.
- * @param lockWaitTimeout length of time to wait in
- * milliseconds or {@link
- * #LOCK_OBTAIN_WAIT_FOREVER} to retry forever
- * @return true if lock was obtained
- * @throws LockObtainFailedException if lock wait times out
- * @throws IllegalArgumentException if lockWaitTimeout is
- * out of bounds
- * @throws IOException if obtain() throws IOException
- */
- public final boolean obtain(long lockWaitTimeout) throws IOException {
- failureReason = null;
- boolean locked = obtain();
- if (lockWaitTimeout < 0 && lockWaitTimeout != LOCK_OBTAIN_WAIT_FOREVER)
- throw new IllegalArgumentException("lockWaitTimeout should be LOCK_OBTAIN_WAIT_FOREVER or a non-negative number (got " + lockWaitTimeout + ")");
-
- long maxSleepCount = lockWaitTimeout / LOCK_POLL_INTERVAL;
- long sleepCount = 0;
- while (!locked) {
- if (lockWaitTimeout != LOCK_OBTAIN_WAIT_FOREVER && sleepCount++ >= maxSleepCount) {
- String reason = "Lock obtain timed out: " + this.toString();
- if (failureReason != null) {
- reason += ": " + failureReason;
- }
- throw new LockObtainFailedException(reason, failureReason);
- }
- try {
- Thread.sleep(LOCK_POLL_INTERVAL);
- } catch (InterruptedException ie) {
- throw new ThreadInterruptedException(ie);
- }
- locked = obtain();
- }
- return locked;
- }
-
- /** Releases exclusive access. */
public abstract void close() throws IOException;
-
- /** Returns true if the resource is currently locked. Note that one must
- * still call {@link #obtain()} before using the resource. */
- public abstract boolean isLocked() throws IOException;
-
-
- /** Utility class for executing code with exclusive access. */
- public abstract static class With {
- private Lock lock;
- private long lockWaitTimeout;
-
-
- /** Constructs an executor that will grab the named lock. */
- public With(Lock lock, long lockWaitTimeout) {
- this.lock = lock;
- this.lockWaitTimeout = lockWaitTimeout;
- }
-
- /** Code to execute with exclusive access. */
- protected abstract Object doBody() throws IOException;
-
- /** Calls {@link #doBody} while <i>lock</i> is obtained. Blocks if lock
- * cannot be obtained immediately. Retries to obtain lock once per second
- * until it is obtained, or until it has tried ten times. Lock is released when
- * {@link #doBody} exits.
- * @throws LockObtainFailedException if lock could not
- * be obtained
- * @throws IOException if {@link Lock#obtain} throws IOException
- */
- public Object run() throws IOException {
- boolean locked = false;
- try {
- locked = lock.obtain(lockWaitTimeout);
- return doBody();
- } finally {
- if (locked) {
- lock.close();
- }
- }
- }
- }
-
+
+ /**
+ * Best effort check that this lock is still valid. Locks
+ * could become invalidated externally for a number of reasons,
+ * for example if a user deletes the lock file manually or
+ * when a network filesystem is in use.
+ * @throws IOException if the lock is no longer valid.
+ */
+ public abstract void ensureValid() throws IOException;
}
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockFactory.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockFactory.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockFactory.java Thu Jun 4 18:20:44 2015
@@ -17,6 +17,7 @@ package org.apache.lucene.store;
* limitations under the License.
*/
+import java.io.IOException;
/**
* <p>Base class for Locking implementation. {@link Directory} uses
@@ -46,9 +47,12 @@ package org.apache.lucene.store;
public abstract class LockFactory {
/**
- * Return a new Lock instance identified by lockName.
+ * Return a new obtained Lock instance identified by lockName.
* @param lockName name of the lock to be created.
+ * @throws LockObtainFailedException (optional specific exception) if the lock could
+ * not be obtained because it is currently held elsewhere.
+ * @throws IOException if any i/o error occurs attempting to gain the lock
*/
- public abstract Lock makeLock(Directory dir, String lockName);
+ public abstract Lock obtainLock(Directory dir, String lockName) throws IOException;
}
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockObtainFailedException.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockObtainFailedException.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockObtainFailedException.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockObtainFailedException.java Thu Jun 4 18:20:44 2015
@@ -24,7 +24,7 @@ import java.io.IOException;
* could not be acquired. This
* happens when a writer tries to open an index
* that another writer already has open.
- * @see Lock#obtain(long)
+ * @see LockFactory#obtainLock(Directory, String)
*/
public class LockObtainFailedException extends IOException {
public LockObtainFailedException(String message) {
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockStressTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockStressTest.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockStressTest.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockStressTest.java Thu Jun 4 18:20:44 2015
@@ -38,10 +38,12 @@ import org.apache.lucene.util.SuppressFo
*/
public class LockStressTest {
+
+ static final String LOCK_FILE_NAME = "test.lock";
@SuppressForbidden(reason = "System.out required: command line tool")
+ @SuppressWarnings("try")
public static void main(String[] args) throws Exception {
-
if (args.length != 7) {
System.out.println("Usage: java org.apache.lucene.store.LockStressTest myID verifierHost verifierPort lockFactoryClassName lockDirName sleepTimeMS count\n" +
"\n" +
@@ -91,7 +93,6 @@ public class LockStressTest {
out.write(myID);
out.flush();
LockFactory verifyLF = new VerifyingLockFactory(lockFactory, in, out);
- Lock l = verifyLF.makeLock(lockDir, "test.lock");
final Random rnd = new Random();
// wait for starting gun
@@ -100,25 +101,22 @@ public class LockStressTest {
}
for (int i = 0; i < count; i++) {
- boolean obtained = false;
- try {
- obtained = l.obtain(rnd.nextInt(100) + 10);
- } catch (LockObtainFailedException e) {}
-
- if (obtained) {
+ try (final Lock l = verifyLF.obtainLock(lockDir, LOCK_FILE_NAME)) {
if (rnd.nextInt(10) == 0) {
if (rnd.nextBoolean()) {
verifyLF = new VerifyingLockFactory(getNewLockFactory(lockFactoryClassName), in, out);
}
- final Lock secondLock = verifyLF.makeLock(lockDir, "test.lock");
- if (secondLock.obtain()) {
- throw new IOException("Double Obtain");
+ try (final Lock secondLock = verifyLF.obtainLock(lockDir, LOCK_FILE_NAME)) {
+ throw new IOException("Double obtain");
+ } catch (LockObtainFailedException loe) {
+ // pass
}
}
Thread.sleep(sleepTimeMS);
- l.close();
+ } catch (LockObtainFailedException loe) {
+ // obtain failed
}
-
+
if (i % 500 == 0) {
System.out.println((i * 100. / count) + "% done.");
}
Added: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockValidatingDirectoryWrapper.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockValidatingDirectoryWrapper.java?rev=1683606&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockValidatingDirectoryWrapper.java (added)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/LockValidatingDirectoryWrapper.java Thu Jun 4 18:20:44 2015
@@ -0,0 +1,64 @@
+package org.apache.lucene.store;
+
+/*
+ * 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 java.io.IOException;
+import java.util.Collection;
+
+/**
+ * This class makes a best-effort check that a provided {@link Lock}
+ * is valid before any destructive filesystem operation.
+ */
+public final class LockValidatingDirectoryWrapper extends FilterDirectory {
+ private final Lock writeLock;
+
+ public LockValidatingDirectoryWrapper(Directory in, Lock writeLock) {
+ super(in);
+ this.writeLock = writeLock;
+ }
+
+ @Override
+ public void deleteFile(String name) throws IOException {
+ writeLock.ensureValid();
+ in.deleteFile(name);
+ }
+
+ @Override
+ public IndexOutput createOutput(String name, IOContext context) throws IOException {
+ writeLock.ensureValid();
+ return in.createOutput(name, context);
+ }
+
+ @Override
+ public void copyFrom(Directory from, String src, String dest, IOContext context) throws IOException {
+ writeLock.ensureValid();
+ in.copyFrom(from, src, dest, context);
+ }
+
+ @Override
+ public void renameFile(String source, String dest) throws IOException {
+ writeLock.ensureValid();
+ in.renameFile(source, dest);
+ }
+
+ @Override
+ public void sync(Collection<String> names) throws IOException {
+ writeLock.ensureValid();
+ in.sync(names);
+ }
+}
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/NativeFSLockFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/NativeFSLockFactory.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/NativeFSLockFactory.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/NativeFSLockFactory.java Thu Jun 4 18:20:44 2015
@@ -18,10 +18,12 @@ package org.apache.lucene.store;
*/
import java.nio.channels.FileChannel;
-import java.nio.channels.OverlappingFileLockException;
+import java.nio.channels.FileLock;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
@@ -77,136 +79,127 @@ public final class NativeFSLockFactory e
*/
public static final NativeFSLockFactory INSTANCE = new NativeFSLockFactory();
+ private static final Set<String> LOCK_HELD = Collections.synchronizedSet(new HashSet<String>());
+
private NativeFSLockFactory() {}
@Override
- protected Lock makeFSLock(FSDirectory dir, String lockName) {
- return new NativeFSLock(dir.getDirectory(), lockName);
+ protected Lock obtainFSLock(FSDirectory dir, String lockName) throws IOException {
+ Path lockDir = dir.getDirectory();
+
+ // Ensure that lockDir exists and is a directory.
+ // note: this will fail if lockDir is a symlink
+ Files.createDirectories(lockDir);
+
+ Path lockFile = lockDir.resolve(lockName);
+
+ try {
+ Files.createFile(lockFile);
+ } catch (IOException ignore) {
+ // we must create the file to have a truly canonical path.
+ // if it's already created, we don't care. if it cant be created, it will fail below.
+ }
+
+ // fails if the lock file does not exist
+ final Path realPath = lockFile.toRealPath();
+
+ // used as a best-effort check, to see if the underlying file has changed
+ final FileTime creationTime = Files.readAttributes(realPath, BasicFileAttributes.class).creationTime();
+
+ if (LOCK_HELD.add(realPath.toString())) {
+ FileChannel channel = null;
+ FileLock lock = null;
+ try {
+ channel = FileChannel.open(realPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
+ lock = channel.tryLock();
+ if (lock != null) {
+ return new NativeFSLock(lock, channel, realPath, creationTime);
+ } else {
+ throw new LockObtainFailedException("Lock held by another program: " + realPath);
+ }
+ } finally {
+ if (lock == null) { // not successful - clear up and move out
+ IOUtils.closeWhileHandlingException(channel); // TODO: addSuppressed
+ clearLockHeld(realPath); // clear LOCK_HELD last
+ }
+ }
+ } else {
+ throw new LockObtainFailedException("Lock held by this virtual machine: " + realPath);
+ }
}
- static final class NativeFSLock extends Lock {
-
- private final Path path;
- private final Path lockDir;
- private static final Set<String> LOCK_HELD = Collections.synchronizedSet(new HashSet<String>());
-
- private FileChannel channel; // set when we have the lock
- private Path realPath; // unconditionally set in obtain(), for use in close()
-
- public NativeFSLock(Path lockDir, String lockFileName) {
- this.lockDir = lockDir;
- path = lockDir.resolve(lockFileName);
+ private static final void clearLockHeld(Path path) throws IOException {
+ boolean remove = LOCK_HELD.remove(path.toString());
+ if (remove == false) {
+ throw new AlreadyClosedException("Lock path was cleared but never marked as held: " + path);
}
+ }
- @Override
- public synchronized boolean obtain() throws IOException {
-
- if (channel != null) {
- // Our instance is already locked:
- assert channel.isOpen();
- assert realPath != null;
- throw new LockObtainFailedException("this lock instance was already obtained");
- }
+ // TODO: kind of bogus we even pass channel:
+ // FileLock has an accessor, but mockfs doesnt yet mock the locks, too scary atm.
- // Ensure that lockDir exists and is a directory.
- Files.createDirectories(lockDir);
- try {
- Files.createFile(path);
- } catch (IOException ignore) {
- // we must create the file to have a truly canonical path.
- // if it's already created, we don't care. if it cant be created, it will fail below.
- }
- realPath = path.toRealPath();
- // Make sure nobody else in-process has this lock held
- // already, and, mark it held if not:
- // This is a pretty crazy workaround for some documented
- // but yet awkward JVM behavior:
- //
- // On some systems, closing a channel releases all locks held by the Java virtual machine on the underlying file
- // regardless of whether the locks were acquired via that channel or via another channel open on the same file.
- // It is strongly recommended that, within a program, a unique channel be used to acquire all locks on any given
- // file.
- //
- // This essentially means if we close "A" channel for a given file all locks might be released... the odd part
- // is that we can't re-obtain the lock in the same JVM but from a different process if that happens. Nevertheless
- // this is super trappy. See LUCENE-5738
- boolean obtained = false;
- if (LOCK_HELD.add(realPath.toString())) {
- FileChannel ch = null;
- try {
- ch = FileChannel.open(realPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
- try {
- if (ch.tryLock() != null) {
- channel = ch;
- obtained = true;
- }
- } catch (IOException | OverlappingFileLockException e) {
- // At least on OS X, we will sometimes get an
- // intermittent "Permission Denied" IOException,
- // which seems to simply mean "you failed to get
- // the lock". But other IOExceptions could be
- // "permanent" (eg, locking is not supported via
- // the filesystem). So, we record the failure
- // reason here; the timeout obtain (usually the
- // one calling us) will use this as "root cause"
- // if it fails to get the lock.
- failureReason = e;
- }
- } finally {
- if (obtained == false) { // not successful - clear up and move out
- IOUtils.closeWhileHandlingException(ch);
- clearLockHeld(realPath); // clear LOCK_HELD last
- }
- }
- }
- return obtained;
+ static final class NativeFSLock extends Lock {
+ final FileLock lock;
+ final FileChannel channel;
+ final Path path;
+ final FileTime creationTime;
+ volatile boolean closed;
+
+ NativeFSLock(FileLock lock, FileChannel channel, Path path, FileTime creationTime) {
+ this.lock = lock;
+ this.channel = channel;
+ this.path = path;
+ this.creationTime = creationTime;
}
@Override
- public synchronized void close() throws IOException {
- if (channel != null) {
- try {
- IOUtils.close(channel);
- } finally {
- channel = null;
- clearLockHeld(realPath); // clear LOCK_HELD last
- }
+ public void ensureValid() throws IOException {
+ if (closed) {
+ throw new AlreadyClosedException("Lock instance already released: " + this);
+ }
+ // check we are still in the locks map (some debugger or something crazy didn't remove us)
+ if (!LOCK_HELD.contains(path.toString())) {
+ throw new AlreadyClosedException("Lock path unexpectedly cleared from map: " + this);
+ }
+ // check our lock wasn't invalidated.
+ if (!lock.isValid()) {
+ throw new AlreadyClosedException("FileLock invalidated by an external force: " + this);
+ }
+ // try to validate the underlying file descriptor.
+ // this will throw IOException if something is wrong.
+ long size = channel.size();
+ if (size != 0) {
+ throw new AlreadyClosedException("Unexpected lock file size: " + size + ", (lock=" + this + ")");
+ }
+ // try to validate the backing file name, that it still exists,
+ // and has the same creation time as when we obtained the lock.
+ // if it differs, someone deleted our lock file (and we are ineffective)
+ FileTime ctime = Files.readAttributes(path, BasicFileAttributes.class).creationTime();
+ if (!creationTime.equals(ctime)) {
+ throw new AlreadyClosedException("Underlying file changed by an external force at " + creationTime + ", (lock=" + this + ")");
}
}
- private static final void clearLockHeld(Path path) {
- boolean remove = LOCK_HELD.remove(path.toString());
- assert remove : "Lock was cleared but never marked as held";
- }
-
@Override
- public synchronized boolean isLocked() {
- // The test for is isLocked is not directly possible with native file locks:
-
- // First a shortcut, if a lock reference in this instance is available
- if (channel != null) {
- return true;
- }
-
- // Look if lock file is definitely not present; if not, there can definitely be no lock!
- if (Files.notExists(path)) {
- return false;
+ public synchronized void close() throws IOException {
+ if (closed) {
+ return;
+ }
+ // NOTE: we don't validate, as unlike SimpleFSLockFactory, we can't break others locks
+ // first release the lock, then the channel
+ try (FileChannel channel = this.channel;
+ FileLock lock = this.lock) {
+ assert lock != null;
+ assert channel != null;
+ } finally {
+ closed = true;
+ clearLockHeld(path);
}
-
- // Try to obtain and release (if was locked) the lock
- try {
- boolean obtained = obtain();
- if (obtained) close();
- return !obtained;
- } catch (IOException ioe) {
- return false;
- }
}
@Override
public String toString() {
- return "NativeFSLock@" + path;
+ return "NativeFSLock(path=" + path + ",impl=" + lock + ",ctime=" + creationTime + ")";
}
}
-
}
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/NoLockFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/NoLockFactory.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/NoLockFactory.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/NoLockFactory.java Thu Jun 4 18:20:44 2015
@@ -37,23 +37,17 @@ public final class NoLockFactory extends
private NoLockFactory() {}
@Override
- public Lock makeLock(Directory dir, String lockName) {
+ public Lock obtainLock(Directory dir, String lockName) {
return SINGLETON_LOCK;
}
private static class NoLock extends Lock {
@Override
- public boolean obtain() throws IOException {
- return true;
- }
-
- @Override
public void close() {
}
@Override
- public boolean isLocked() {
- return false;
+ public void ensureValid() throws IOException {
}
@Override
@@ -61,5 +55,4 @@ public final class NoLockFactory extends
return "NoLock";
}
}
-
}
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/SimpleFSLockFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/SimpleFSLockFactory.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/SimpleFSLockFactory.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/SimpleFSLockFactory.java Thu Jun 4 18:20:44 2015
@@ -17,26 +17,24 @@ package org.apache.lucene.store;
* limitations under the License.
*/
-import java.io.File;
import java.io.IOException;
+import java.nio.file.AccessDeniedException;
+import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
/**
* <p>Implements {@link LockFactory} using {@link
* Files#createFile}.</p>
*
- * <p><b>NOTE:</b> the {@linkplain File#createNewFile() javadocs
- * for <code>File.createNewFile()</code>} contain a vague
- * yet spooky warning about not using the API for file
- * locking. This warning was added due to <a target="_top"
- * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4676183">this
- * bug</a>, and in fact the only known problem with using
- * this API for locking is that the Lucene write lock may
- * not be released when the JVM exits abnormally.</p>
-
- * <p>When this happens, a {@link LockObtainFailedException}
- * is hit when trying to create a writer, in which case you
+ * <p>The main downside with using this API for locking is
+ * that the Lucene write lock may not be released when
+ * the JVM exits abnormally.</p>
+ *
+ * <p>When this happens, an {@link LockObtainFailedException}
+ * is hit when trying to create a writer, in which case you may
* need to explicitly clear the lock file first by
* manually removing the file. But, first be certain that
* no writer is in fact writing to the index otherwise you
@@ -70,66 +68,83 @@ public final class SimpleFSLockFactory e
private SimpleFSLockFactory() {}
@Override
- protected Lock makeFSLock(FSDirectory dir, String lockName) {
- return new SimpleFSLock(dir.getDirectory(), lockName);
+ protected Lock obtainFSLock(FSDirectory dir, String lockName) throws IOException {
+ Path lockDir = dir.getDirectory();
+
+ // Ensure that lockDir exists and is a directory.
+ // note: this will fail if lockDir is a symlink
+ Files.createDirectories(lockDir);
+
+ Path lockFile = lockDir.resolve(lockName);
+
+ // create the file: this will fail if it already exists
+ try {
+ Files.createFile(lockFile);
+ } catch (FileAlreadyExistsException | AccessDeniedException e) {
+ // convert optional specific exception to our optional specific exception
+ throw new LockObtainFailedException("Lock held elsewhere: " + lockFile, e);
+ }
+
+ // used as a best-effort check, to see if the underlying file has changed
+ final FileTime creationTime = Files.readAttributes(lockFile, BasicFileAttributes.class).creationTime();
+
+ return new SimpleFSLock(lockFile, creationTime);
}
- static class SimpleFSLock extends Lock {
-
- Path lockFile;
- Path lockDir;
- boolean obtained = false;
-
- public SimpleFSLock(Path lockDir, String lockFileName) {
- this.lockDir = lockDir;
- lockFile = lockDir.resolve(lockFileName);
+ static final class SimpleFSLock extends Lock {
+ private final Path path;
+ private final FileTime creationTime;
+ private volatile boolean closed;
+
+ SimpleFSLock(Path path, FileTime creationTime) throws IOException {
+ this.path = path;
+ this.creationTime = creationTime;
}
@Override
- public synchronized boolean obtain() throws IOException {
- if (obtained) {
- // Our instance is already locked:
- throw new LockObtainFailedException("this lock instance was already obtained");
+ public void ensureValid() throws IOException {
+ if (closed) {
+ throw new AlreadyClosedException("Lock instance already released: " + this);
}
-
- try {
- Files.createDirectories(lockDir);
- Files.createFile(lockFile);
- obtained = true;
- } catch (IOException ioe) {
- // On Windows, on concurrent createNewFile, the 2nd process gets "access denied".
- // In that case, the lock was not aquired successfully, so return false.
- // We record the failure reason here; the obtain with timeout (usually the
- // one calling us) will use this as "root cause" if it fails to get the lock.
- failureReason = ioe;
+ // try to validate the backing file name, that it still exists,
+ // and has the same creation time as when we obtained the lock.
+ // if it differs, someone deleted our lock file (and we are ineffective)
+ FileTime ctime = Files.readAttributes(path, BasicFileAttributes.class).creationTime();
+ if (!creationTime.equals(ctime)) {
+ throw new AlreadyClosedException("Underlying file changed by an external force at " + creationTime + ", (lock=" + this + ")");
}
-
- return obtained;
}
@Override
- public synchronized void close() throws LockReleaseFailedException {
- // TODO: wierd that clearLock() throws the raw IOException...
- if (obtained) {
+ public synchronized void close() throws IOException {
+ if (closed) {
+ return;
+ }
+ try {
+ // NOTE: unlike NativeFSLockFactory, we can potentially delete someone else's
+ // lock if things have gone wrong. we do best-effort check (ensureValid) to
+ // avoid doing this.
+ try {
+ ensureValid();
+ } catch (Throwable exc) {
+ // notify the user they may need to intervene.
+ throw new LockReleaseFailedException("Lock file cannot be safely removed. Manual intervention is recommended.", exc);
+ }
+ // we did a best effort check, now try to remove the file. if something goes wrong,
+ // we need to make it clear to the user that the directory may still remain locked.
try {
- Files.deleteIfExists(lockFile);
- } catch (Throwable cause) {
- throw new LockReleaseFailedException("failed to delete " + lockFile, cause);
- } finally {
- obtained = false;
+ Files.delete(path);
+ } catch (Throwable exc) {
+ throw new LockReleaseFailedException("Unable to remove lock file. Manual intervention is recommended", exc);
}
+ } finally {
+ closed = true;
}
}
@Override
- public boolean isLocked() {
- return Files.exists(lockFile);
- }
-
- @Override
public String toString() {
- return "SimpleFSLock@" + lockFile;
+ return "SimpleFSLock(path=" + path + ",ctime=" + creationTime + ")";
}
}
-
}
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/SingleInstanceLockFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/SingleInstanceLockFactory.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/SingleInstanceLockFactory.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/SingleInstanceLockFactory.java Thu Jun 4 18:20:44 2015
@@ -24,7 +24,7 @@ import java.util.HashSet;
* Implements {@link LockFactory} for a single in-process instance,
* meaning all locking will take place through this one instance.
* Only use this {@link LockFactory} when you are certain all
- * IndexReaders and IndexWriters for a given index are running
+ * IndexWriters for a given index are running
* against a single shared in-process Directory instance. This is
* currently the default locking for RAMDirectory.
*
@@ -33,51 +33,53 @@ import java.util.HashSet;
public final class SingleInstanceLockFactory extends LockFactory {
- private final HashSet<String> locks = new HashSet<>();
+ final HashSet<String> locks = new HashSet<>();
@Override
- public Lock makeLock(Directory dir, String lockName) {
- return new SingleInstanceLock(locks, lockName);
+ public Lock obtainLock(Directory dir, String lockName) throws IOException {
+ synchronized (locks) {
+ if (locks.add(lockName)) {
+ return new SingleInstanceLock(lockName);
+ } else {
+ throw new LockObtainFailedException("lock instance already obtained: (dir=" + dir + ", lockName=" + lockName + ")");
+ }
+ }
}
- private static class SingleInstanceLock extends Lock {
-
+ private class SingleInstanceLock extends Lock {
private final String lockName;
- private final HashSet<String> locks;
- private boolean obtained = false;
+ private volatile boolean closed;
- public SingleInstanceLock(HashSet<String> locks, String lockName) {
- this.locks = locks;
+ public SingleInstanceLock(String lockName) {
this.lockName = lockName;
}
@Override
- public boolean obtain() throws IOException {
- synchronized(locks) {
- if (obtained) {
- // Our instance is already locked:
- throw new LockObtainFailedException("this lock instance was already obtained");
- }
- obtained = locks.add(lockName);
-
- return obtained;
+ public void ensureValid() throws IOException {
+ if (closed) {
+ throw new AlreadyClosedException("Lock instance already released: " + this);
}
- }
-
- @Override
- public void close() {
- synchronized(locks) {
- if (obtained) {
- locks.remove(lockName);
- obtained = false;
+ // check we are still in the locks map (some debugger or something crazy didn't remove us)
+ synchronized (locks) {
+ if (!locks.contains(lockName)) {
+ throw new AlreadyClosedException("Lock instance was invalidated from map: " + this);
}
}
}
@Override
- public boolean isLocked() {
- synchronized(locks) {
- return locks.contains(lockName);
+ public synchronized void close() throws IOException {
+ if (closed) {
+ return;
+ }
+ try {
+ synchronized (locks) {
+ if (!locks.remove(lockName)) {
+ throw new AlreadyClosedException("Lock was already released: " + this);
+ }
+ }
+ } finally {
+ closed = true;
}
}
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/VerifyingLockFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/VerifyingLockFactory.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/VerifyingLockFactory.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/store/VerifyingLockFactory.java Thu Jun 4 18:20:44 2015
@@ -43,10 +43,23 @@ public final class VerifyingLockFactory
private class CheckedLock extends Lock {
private final Lock lock;
- private boolean obtained = false;
- public CheckedLock(Lock lock) {
+ public CheckedLock(Lock lock) throws IOException {
this.lock = lock;
+ verify((byte) 1);
+ }
+
+ @Override
+ public void ensureValid() throws IOException {
+ lock.ensureValid();
+ }
+
+ @Override
+ public void close() throws IOException {
+ try (Lock l = lock) {
+ l.ensureValid();
+ verify((byte) 0);
+ }
}
private void verify(byte message) throws IOException {
@@ -60,29 +73,6 @@ public final class VerifyingLockFactory
throw new IOException("Protocol violation.");
}
}
-
- @Override
- public synchronized boolean obtain() throws IOException {
- obtained = lock.obtain();
- if (obtained) {
- verify((byte) 1);
- }
- return obtained;
- }
-
- @Override
- public synchronized boolean isLocked() throws IOException {
- return lock.isLocked();
- }
-
- @Override
- public synchronized void close() throws IOException {
- if (obtained) {
- assert isLocked();
- verify((byte) 0);
- }
- lock.close();
- }
}
/**
@@ -97,7 +87,7 @@ public final class VerifyingLockFactory
}
@Override
- public Lock makeLock(Directory dir, String lockName) {
- return new CheckedLock(lf.makeLock(dir, lockName));
+ public Lock obtainLock(Directory dir, String lockName) throws IOException {
+ return new CheckedLock(lf.obtainLock(dir, lockName));
}
}
Modified: lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestAddIndexes.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestAddIndexes.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestAddIndexes.java (original)
+++ lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestAddIndexes.java Thu Jun 4 18:20:44 2015
@@ -1268,7 +1268,6 @@ public class TestAddIndexes extends Luce
Directory dest = newDirectory();
IndexWriterConfig iwc = newIndexWriterConfig(new MockAnalyzer(random()));
- iwc.setWriteLockTimeout(1);
RandomIndexWriter w2 = new RandomIndexWriter(random(), dest, iwc);
try {
Modified: lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriter.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriter.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriter.java (original)
+++ lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriter.java Thu Jun 4 18:20:44 2015
@@ -96,14 +96,7 @@ public class TestIndexWriter extends Luc
IndexReader reader = null;
int i;
- long savedWriteLockTimeout = IndexWriterConfig.getDefaultWriteLockTimeout();
- try {
- IndexWriterConfig.setDefaultWriteLockTimeout(2000);
- assertEquals(2000, IndexWriterConfig.getDefaultWriteLockTimeout());
- writer = new IndexWriter(dir, newIndexWriterConfig(new MockAnalyzer(random())));
- } finally {
- IndexWriterConfig.setDefaultWriteLockTimeout(savedWriteLockTimeout);
- }
+ writer = new IndexWriter(dir, newIndexWriterConfig(new MockAnalyzer(random())));
// add 100 documents
for (i = 0; i < 100; i++) {
@@ -1725,8 +1718,7 @@ public class TestIndexWriter extends Luc
RandomIndexWriter w1 = new RandomIndexWriter(random(), d);
w1.deleteAll();
try {
- new RandomIndexWriter(random(), d, newIndexWriterConfig(null)
- .setWriteLockTimeout(100));
+ new RandomIndexWriter(random(), d, newIndexWriterConfig(null));
fail("should not be able to create another writer");
} catch (LockObtainFailedException lofe) {
// expected
Modified: lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterExceptions.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterExceptions.java?rev=1683606&r1=1683605&r2=1683606&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterExceptions.java (original)
+++ lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterExceptions.java Thu Jun 4 18:20:44 2015
@@ -2199,7 +2199,7 @@ public class TestIndexWriterExceptions e
// even though we hit exception: we are closed, no locks or files held, index in good state
assertTrue(iw.isClosed());
- assertFalse(IndexWriter.isLocked(dir));
+ dir.obtainLock(IndexWriter.WRITE_LOCK_NAME).close();
r = DirectoryReader.open(dir);
assertEquals(10, r.maxDoc());
@@ -2268,7 +2268,7 @@ public class TestIndexWriterExceptions e
// even though we hit exception: we are closed, no locks or files held, index in good state
assertTrue(iw.isClosed());
- assertFalse(IndexWriter.isLocked(dir));
+ dir.obtainLock(IndexWriter.WRITE_LOCK_NAME).close();
r = DirectoryReader.open(dir);
assertEquals(10, r.maxDoc());
Added: lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestSleepingLockWrapper.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestSleepingLockWrapper.java?rev=1683606&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestSleepingLockWrapper.java (added)
+++ lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestSleepingLockWrapper.java Thu Jun 4 18:20:44 2015
@@ -0,0 +1,49 @@
+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 java.io.IOException;
+import java.nio.file.Path;
+
+import org.apache.lucene.index.SleepingLockWrapper;
+import org.apache.lucene.store.BaseLockFactoryTestCase;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.SingleInstanceLockFactory;
+import org.apache.lucene.util.TestUtil;
+
+/** Simple tests for SleepingLockWrapper */
+public class TestSleepingLockWrapper extends BaseLockFactoryTestCase {
+
+ @Override
+ protected Directory getDirectory(Path path) throws IOException {
+ long lockWaitTimeout = TestUtil.nextLong(random(), 20, 100);
+ long pollInterval = TestUtil.nextLong(random(), 2, 10);
+
+ int which = random().nextInt(3);
+ switch (which) {
+ case 0:
+ return new SleepingLockWrapper(newDirectory(random(), new SingleInstanceLockFactory()), lockWaitTimeout, pollInterval);
+ case 1:
+ return new SleepingLockWrapper(newFSDirectory(path), lockWaitTimeout, pollInterval);
+ default:
+ return new SleepingLockWrapper(newFSDirectory(path), lockWaitTimeout, pollInterval);
+ }
+ }
+
+ // TODO: specific tests to this impl
+}