You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by mi...@apache.org on 2015/07/14 11:38:51 UTC
svn commit: r1690886 - in /lucene/dev/trunk/lucene: ./
core/src/java/org/apache/lucene/index/ core/src/test/org/apache/lucene/
core/src/test/org/apache/lucene/index/
sandbox/src/test/org/apache/lucene/codecs/idversion/
Author: mikemccand
Date: Tue Jul 14 09:38:50 2015
New Revision: 1690886
URL: http://svn.apache.org/r1690886
Log:
LUCENE-6579: exceptions during merging are now tragic
Modified:
lucene/dev/trunk/lucene/CHANGES.txt
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/ConcurrentMergeScheduler.java
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java
lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/TestMergeSchedulerExternal.java
lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexFileDeleter.java
lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterDelete.java
lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterExceptions.java
lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterOnDiskFull.java
lucene/dev/trunk/lucene/sandbox/src/test/org/apache/lucene/codecs/idversion/TestIDVersionPostingsFormat.java
Modified: lucene/dev/trunk/lucene/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/CHANGES.txt?rev=1690886&r1=1690885&r2=1690886&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/CHANGES.txt (original)
+++ lucene/dev/trunk/lucene/CHANGES.txt Tue Jul 14 09:38:50 2015
@@ -301,6 +301,10 @@ Changes in Runtime Behavior
"used" when the first Scorer is pulled instead of when a Scorer is pulled on
the first segment on an index. (Terry Smith, Adrien Grand)
+* LUCENE-6579: IndexWriter now sacrifices (closes) itself to protect the index
+ when an unexpected, tragic exception strikes while merging. (Robert
+ Muir, Mike McCandless)
+
Optimizations
* LUCENE-6548: Some optimizations for BlockTree's intersect with very
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/ConcurrentMergeScheduler.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/ConcurrentMergeScheduler.java?rev=1690886&r1=1690885&r2=1690886&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/ConcurrentMergeScheduler.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/index/ConcurrentMergeScheduler.java Tue Jul 14 09:38:50 2015
@@ -414,7 +414,9 @@ public class ConcurrentMergeScheduler ex
MergeThread toSync = null;
synchronized (this) {
for (MergeThread t : mergeThreads) {
- if (t.isAlive()) {
+ // In case a merge thread is calling us, don't try to sync on
+ // itself, since that will never finish!
+ if (t.isAlive() && t != Thread.currentThread()) {
toSync = t;
break;
}
@@ -663,18 +665,6 @@ public class ConcurrentMergeScheduler ex
/** Called when an exception is hit in a background merge
* thread */
protected void handleMergeException(Directory dir, Throwable exc) {
- try {
- // When an exception is hit during merge, IndexWriter
- // removes any partial files and then allows another
- // merge to run. If whatever caused the error is not
- // transient then the exception will keep happening,
- // so, we sleep here to avoid saturating CPU in such
- // cases:
- Thread.sleep(250);
- } catch (InterruptedException ie) {
- throw new ThreadInterruptedException(ie);
- }
-
throw new MergePolicy.MergeException(exc, dir);
}
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=1690886&r1=1690885&r2=1690886&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 Tue Jul 14 09:38:50 2015
@@ -1055,7 +1055,7 @@ public class IndexWriter implements Clos
}
// Ensure that only one thread actually gets to do the
// closing
- if (shouldClose()) {
+ if (shouldClose(true)) {
boolean success = false;
try {
if (infoStream.isEnabled("IW")) {
@@ -1113,14 +1113,17 @@ public class IndexWriter implements Clos
}
// Returns true if this thread should attempt to close, or
- // false if IndexWriter is now closed; else, waits until
- // another thread finishes closing
- synchronized private boolean shouldClose() {
- while(true) {
- if (!closed) {
- if (!closing) {
+ // false if IndexWriter is now closed; else,
+ // waits until another thread finishes closing
+ synchronized private boolean shouldClose(boolean waitForClose) {
+ while (true) {
+ if (closed == false) {
+ if (closing == false) {
+ // We get to close
closing = true;
return true;
+ } else if (waitForClose == false) {
+ return false;
} else {
// Another thread is presently trying to close;
// wait until it finishes one way (closes
@@ -2012,7 +2015,7 @@ public class IndexWriter implements Clos
// Ensure that only one thread actually gets to do the
// closing, and make sure no commit is also in progress:
synchronized(commitLock) {
- if (shouldClose()) {
+ if (shouldClose(true)) {
rollbackInternal();
}
}
@@ -2027,10 +2030,7 @@ public class IndexWriter implements Clos
}
try {
- synchronized(this) {
- abortMerges();
- stopMerges = true;
- }
+ abortMerges();
rateLimiters.close();
@@ -2039,7 +2039,7 @@ public class IndexWriter implements Clos
}
// Must pre-close in case it increments changeCount so that we can then
- // set it to false before calling closeInternal
+ // set it to false before calling rollbackInternal
mergeScheduler.close();
bufferedUpdatesStream.clear();
@@ -2061,11 +2061,10 @@ public class IndexWriter implements Clos
readerPool.dropAll(false);
// Keep the same segmentInfos instance but replace all
- // of its SegmentInfo instances. This is so the next
- // attempt to commit using this instance of IndexWriter
- // will always write to a new generation ("write
- // once").
+ // of its SegmentInfo instances so IFD below will remove
+ // any segments we flushed since the last commit:
segmentInfos.rollbackSegmentInfos(rollbackSegments);
+
if (infoStream.isEnabled("IW") ) {
infoStream.message("IW", "rollback: infos=" + segString(segmentInfos));
}
@@ -2073,13 +2072,15 @@ public class IndexWriter implements Clos
testPoint("rollback before checkpoint");
// Ask deleter to locate unreferenced files & remove
- // them:
- deleter.checkpoint(segmentInfos, false);
- deleter.refresh();
+ // them ... only when we are not experiencing a tragedy, else
+ // these methods throw ACE:
+ if (tragedy == null) {
+ deleter.checkpoint(segmentInfos, false);
+ deleter.refresh();
+ deleter.close();
+ }
lastCommitChangeCount = changeCount.get();
-
- deleter.close();
// Must set closed while inside same sync block where we call deleter.refresh, else concurrent threads may try to sneak a flush in,
// after we leave this sync block and before we enter the sync block in the finally clause below that sets closed:
@@ -2093,14 +2094,14 @@ public class IndexWriter implements Clos
} catch (OutOfMemoryError oom) {
tragicEvent(oom, "rollbackInternal");
} finally {
- if (!success) {
+ if (success == false) {
// Must not hold IW's lock while closing
// mergeScheduler: this can lead to deadlock,
// e.g. TestIW.testThreadInterruptDeadlock
IOUtils.closeWhileHandlingException(mergeScheduler);
}
synchronized(this) {
- if (!success) {
+ if (success == false) {
// we tried to be nice about it: do the minimum
// don't leak a segments_N file if there is a pending commit
@@ -2119,6 +2120,9 @@ public class IndexWriter implements Clos
}
closed = true;
closing = false;
+
+ // So any "concurrently closing" threads wake up and see that the close has now completed:
+ notifyAll();
}
}
}
@@ -2176,6 +2180,8 @@ public class IndexWriter implements Clos
try {
// Abort any running merges
abortMerges();
+ // Let merges run again
+ stopMerges = false;
// Remove all segments
pendingNumDocs.addAndGet(-segmentInfos.totalMaxDoc());
segmentInfos.clear();
@@ -2214,6 +2220,7 @@ public class IndexWriter implements Clos
* method: when you abort a long-running merge, you lose
* a lot of work that must later be redone. */
private synchronized void abortMerges() {
+
stopMerges = true;
// Abort all pending & running merges:
@@ -2233,21 +2240,19 @@ public class IndexWriter implements Clos
merge.rateLimiter.setAbort();
}
- // These merges periodically check whether they have
- // been aborted, and stop if so. We wait here to make
- // sure they all stop. It should not take very long
- // because the merge threads periodically check if
+ // We wait here to make all merges stop. It should not
+ // take very long because they periodically check if
// they are aborted.
- while(runningMerges.size() > 0) {
+ while (runningMerges.size() != 0) {
+
if (infoStream.isEnabled("IW")) {
infoStream.message("IW", "now wait for " + runningMerges.size() + " running merge/s to abort");
}
+
doWait();
}
- stopMerges = false;
notifyAll();
-
assert 0 == mergingSegments.size();
if (infoStream.isEnabled("IW")) {
@@ -2274,7 +2279,6 @@ public class IndexWriter implements Clos
infoStream.message("IW", "waitForMerges");
}
-
while (pendingMerges.size() > 0 || runningMerges.size() > 0) {
doWait();
}
@@ -2975,6 +2979,12 @@ public class IndexWriter implements Clos
try {
synchronized(this) {
+ ensureOpen(false);
+
+ if (tragedy != null) {
+ throw new IllegalStateException("this writer hit an unrecoverable error; cannot complete commit", tragedy);
+ }
+
if (pendingCommit != null) {
try {
@@ -3057,6 +3067,7 @@ public class IndexWriter implements Clos
* deletes or docs were flushed) if necessary
* @param applyAllDeletes whether pending deletes should also
*/
+ // why protected
protected final void flush(boolean triggerMerge, boolean applyAllDeletes) throws IOException {
// NOTE: this method cannot be sync'd because
@@ -3596,7 +3607,7 @@ public class IndexWriter implements Clos
if (t instanceof MergePolicy.MergeAbortedException) {
// We can ignore this exception (it happens when
- // close(false) or rollback is called), unless the
+ // deleteAll or rollback is called), unless the
// merge involves segments from external directories,
// in which case we must throw it so, for example, the
// rollbackTransaction code in addIndexes* is
@@ -3654,19 +3665,20 @@ public class IndexWriter implements Clos
if (merge.info != null && !segmentInfos.contains(merge.info)) {
deleter.refresh(merge.info.info.name);
}
- }
-
- // This merge (and, generally, any change to the
- // segments) may now enable new merges, so we call
- // merge policy & update pending merges.
- if (success && merge.rateLimiter.getAbort() == false && (merge.maxNumSegments != -1 || (!closed && !closing))) {
+ } else if (merge.rateLimiter.getAbort() == false && (merge.maxNumSegments != -1 || (!closed && !closing))) {
+ // This merge (and, generally, any change to the
+ // segments) may now enable new merges, so we call
+ // merge policy & update pending merges.
updatePendingMerges(mergePolicy, MergeTrigger.MERGE_FINISHED, merge.maxNumSegments);
}
}
}
- } catch (OutOfMemoryError oom) {
- tragicEvent(oom, "merge");
+ } catch (Throwable t) {
+ // Important that tragicEvent is called after mergeFinish, else we hang
+ // waiting for our merge thread to be removed from runningMerges:
+ tragicEvent(t, "merge");
}
+
if (merge.info != null && merge.rateLimiter.getAbort() == false) {
if (infoStream.isEnabled("IW")) {
infoStream.message("IW", "merge time " + (System.currentTimeMillis()-t0) + " msec for " + merge.info.info.maxDoc() + " docs");
@@ -4260,7 +4272,7 @@ public class IndexWriter implements Clos
* debugging.
*
* @lucene.internal */
- public synchronized String segString() {
+ synchronized String segString() {
return segString(segmentInfos);
}
@@ -4268,7 +4280,7 @@ public class IndexWriter implements Clos
* segments, for debugging.
*
* @lucene.internal */
- public synchronized String segString(Iterable<SegmentCommitInfo> infos) {
+ synchronized String segString(Iterable<SegmentCommitInfo> infos) {
final StringBuilder buffer = new StringBuilder();
for(final SegmentCommitInfo info : infos) {
if (buffer.length() > 0) {
@@ -4283,7 +4295,7 @@ public class IndexWriter implements Clos
* segment, for debugging.
*
* @lucene.internal */
- public synchronized String segString(SegmentCommitInfo info) {
+ synchronized String segString(SegmentCommitInfo info) {
return info.toString(numDeletedDocs(info) - info.getDelCount());
}
@@ -4513,10 +4525,16 @@ public class IndexWriter implements Clos
}
void tragicEvent(Throwable tragedy, String location) throws IOException {
+
// unbox our internal AbortingException
if (tragedy instanceof AbortingException) {
tragedy = tragedy.getCause();
}
+
+ // This is not supposed to be tragic: IW is supposed to catch this and
+ // ignore, because it means we asked the merge to abort:
+ assert tragedy instanceof MergePolicy.MergeAbortedException == false;
+
// We cannot hold IW's lock here else it can lead to deadlock:
assert Thread.holdsLock(this) == false;
@@ -4528,22 +4546,18 @@ public class IndexWriter implements Clos
}
synchronized (this) {
- // it's possible you could have a really bad day
- if (this.tragedy == null) {
- this.tragedy = tragedy;
+ // It's possible you could have a really bad day
+ if (this.tragedy != null) {
+ // Another thread is already dealing / has dealt with the tragedy:
+ IOUtils.reThrow(tragedy);
}
+
+ this.tragedy = tragedy;
}
// if we are already closed (e.g. called by rollback), this will be a no-op.
- synchronized(commitLock) {
- if (closing == false) {
- try {
- rollback();
- } catch (Throwable ignored) {
- // it would be confusing to addSuppressed here, it's unrelated to the disaster,
- // and it's possible our internal state is amiss anyway.
- }
- }
+ if (shouldClose(false)) {
+ rollbackInternal();
}
IOUtils.reThrow(tragedy);
Modified: lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/TestMergeSchedulerExternal.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/TestMergeSchedulerExternal.java?rev=1690886&r1=1690885&r2=1690886&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/TestMergeSchedulerExternal.java (original)
+++ lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/TestMergeSchedulerExternal.java Tue Jul 14 09:38:50 2015
@@ -79,8 +79,9 @@ public class TestMergeSchedulerExternal
public void eval(MockDirectoryWrapper dir) throws IOException {
StackTraceElement[] trace = new Exception().getStackTrace();
for (int i = 0; i < trace.length; i++) {
- if ("doMerge".equals(trace[i].getMethodName()))
+ if ("doMerge".equals(trace[i].getMethodName())) {
throw new IOException("now failing during merge");
+ }
}
}
}
@@ -99,11 +100,16 @@ public class TestMergeSchedulerExternal
.setMergePolicy(newLogMergePolicy()));
LogMergePolicy logMP = (LogMergePolicy) writer.getConfig().getMergePolicy();
logMP.setMergeFactor(10);
- for(int i=0;i<20;i++)
+ for(int i=0;i<20;i++) {
writer.addDocument(doc);
+ }
- ((MyMergeScheduler) writer.getConfig().getMergeScheduler()).sync();
- writer.close();
+ try {
+ ((MyMergeScheduler) writer.getConfig().getMergeScheduler()).sync();
+ } catch (IllegalStateException ise) {
+ // OK
+ }
+ writer.rollback();
assertTrue(mergeThreadCreated);
assertTrue(mergeCalled);
Modified: lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexFileDeleter.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexFileDeleter.java?rev=1690886&r1=1690885&r2=1690886&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexFileDeleter.java (original)
+++ lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexFileDeleter.java Tue Jul 14 09:38:50 2015
@@ -417,7 +417,7 @@ public class TestIndexFileDeleter extend
}
// LUCENE-5919
- public void testExcInDecRef() throws Exception {
+ public void testExcInDecRef() throws Throwable {
MockDirectoryWrapper dir = newMockDirectory();
// disable slow things: we don't rely upon sleeps here.
@@ -484,24 +484,11 @@ public class TestIndexFileDeleter extend
doc.add(newTextField("field", "some text", Field.Store.NO));
w.addDocument(doc);
}
- } catch (IOException ioe) {
- if (ioe.getMessage().contains("background merge hit exception")) {
- Throwable cause = ioe.getCause();
- if (cause != null && cause instanceof RuntimeException && ((RuntimeException) cause).getMessage().equals("fake fail")) {
- // ok
- } else {
- throw ioe;
- }
- } else {
- throw ioe;
- }
- } catch (RuntimeException re) {
- if (re.getMessage().equals("fake fail")) {
+ } catch (Throwable t) {
+ if (t.toString().contains("fake fail") || t.getCause().toString().contains("fake fail")) {
// ok
- } else if (re instanceof AlreadyClosedException && re.getCause() != null && "fake fail".equals(re.getCause().getMessage())) {
- break; // our test got unlucky, triggered our strange exception after successful finishCommit, caused a disaster!
} else {
- throw re;
+ throw t;
}
}
}
Modified: lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterDelete.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterDelete.java?rev=1690886&r1=1690885&r2=1690886&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterDelete.java (original)
+++ lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterDelete.java Tue Jul 14 09:38:50 2015
@@ -598,7 +598,12 @@ public class TestIndexWriterDelete exten
}
docId += 12;
}
- modifier.close();
+ try {
+ modifier.close();
+ } catch (IllegalStateException ise) {
+ // ok
+ throw (IOException) ise.getCause();
+ }
}
success = true;
if (0 == x) {
@@ -846,6 +851,10 @@ public class TestIndexWriterDelete exten
modifier.commit();
} catch (RuntimeException ioe) {
// expected
+ if (VERBOSE) {
+ System.out.println("TEST: hit exc:");
+ ioe.printStackTrace(System.out);
+ }
failed = true;
}
@@ -854,14 +863,23 @@ public class TestIndexWriterDelete exten
// The commit above failed, so we need to retry it (which will
// succeed, because the failure is a one-shot)
- modifier.commit();
+ boolean writerClosed;
+ try {
+ modifier.commit();
+ writerClosed = false;
+ } catch (IllegalStateException ise) {
+ // The above exc struck during merge, and closed the writer
+ writerClosed = true;
+ }
- hitCount = getHitCount(dir, term);
+ if (writerClosed == false) {
+ hitCount = getHitCount(dir, term);
- // Make sure the delete was successfully flushed:
- assertEquals(0, hitCount);
+ // Make sure the delete was successfully flushed:
+ assertEquals(0, hitCount);
- modifier.close();
+ modifier.close();
+ }
dir.close();
}
@@ -1359,6 +1377,40 @@ public class TestIndexWriterDelete exten
r.close();
w.close();
+ dir.close();
+ }
+
+ // Make sure merges still kick off after IW.deleteAll!
+ public void testMergingAfterDeleteAll() throws Exception {
+ Directory dir = newDirectory();
+ IndexWriterConfig iwc = new IndexWriterConfig(new MockAnalyzer(random()));
+ iwc.setMaxBufferedDocs(2);
+ LogDocMergePolicy mp = new LogDocMergePolicy();
+ mp.setMinMergeDocs(1);
+ iwc.setMergePolicy(mp);
+ iwc.setMergeScheduler(new SerialMergeScheduler());
+ IndexWriter w = new IndexWriter(dir, iwc);
+ for(int i=0;i<10;i++) {
+ Document doc = new Document();
+ doc.add(newStringField("id", ""+i, Field.Store.NO));
+ w.addDocument(doc);
+ }
+ w.commit();
+ w.deleteAll();
+
+ for(int i=0;i<100;i++) {
+ Document doc = new Document();
+ doc.add(newStringField("id", ""+i, Field.Store.NO));
+ w.addDocument(doc);
+ }
+
+ w.forceMerge(1);
+
+ DirectoryReader r = DirectoryReader.open(w, true);
+ assertEquals(1, r.leaves().size());
+ r.close();
+
+ w.close();
dir.close();
}
}
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=1690886&r1=1690885&r2=1690886&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 Tue Jul 14 09:38:50 2015
@@ -456,14 +456,19 @@ public class TestIndexWriterExceptions e
testPoint.doFail = true;
Document doc = new Document();
doc.add(newTextField("field", "a field", Field.Store.YES));
- for(int i=0;i<10;i++)
+ for(int i=0;i<10;i++) {
try {
w.addDocument(doc);
} catch (RuntimeException re) {
break;
}
+ }
- ((ConcurrentMergeScheduler) w.getConfig().getMergeScheduler()).sync();
+ try {
+ ((ConcurrentMergeScheduler) w.getConfig().getMergeScheduler()).sync();
+ } catch (IllegalStateException ise) {
+ // OK: merge exc causes tragedy
+ }
assertTrue(testPoint.failed);
w.close();
dir.close();
@@ -974,14 +979,15 @@ public class TestIndexWriterExceptions e
.setMergePolicy(newLogMergePolicy());
((LogMergePolicy) conf.getMergePolicy()).setMergeFactor(100);
IndexWriter w = new IndexWriter(startDir, conf);
- for(int i=0;i<27;i++)
+ for(int i=0;i<27;i++) {
addDoc(w);
+ }
w.close();
int iter = TEST_NIGHTLY ? 200 : 10;
for(int i=0;i<iter;i++) {
if (VERBOSE) {
- System.out.println("TEST: iter " + i);
+ System.out.println("\nTEST: iter " + i);
}
MockDirectoryWrapper dir = new MockDirectoryWrapper(random(), TestUtil.ramCopyOf(startDir));
conf = newIndexWriterConfig(new MockAnalyzer(random()))
@@ -991,12 +997,20 @@ public class TestIndexWriterExceptions e
dir.setRandomIOExceptionRate(0.5);
try {
w.forceMerge(1);
+ } catch (IllegalStateException ise) {
+ // expected
} catch (IOException ioe) {
- if (ioe.getCause() == null)
+ if (ioe.getCause() == null) {
fail("forceMerge threw IOException without root cause");
+ }
}
dir.setRandomIOExceptionRate(0);
- w.close();
+ //System.out.println("TEST: now close IW");
+ try {
+ w.close();
+ } catch (IllegalStateException ise) {
+ // ok
+ }
dir.close();
}
startDir.close();
@@ -2272,4 +2286,72 @@ public class TestIndexWriterExceptions e
dir.close();
}
}
+
+ public void testMergeExceptionIsTragic() throws Exception {
+ MockDirectoryWrapper dir = newMockDirectory();
+ final AtomicBoolean didFail = new AtomicBoolean();
+ dir.failOn(new MockDirectoryWrapper.Failure() {
+
+ @Override
+ public void eval(MockDirectoryWrapper dir) throws IOException {
+ if (random().nextInt(10) != 0) {
+ return;
+ }
+ if (didFail.get()) {
+ // Already failed
+ return;
+ }
+ StackTraceElement[] trace = Thread.currentThread().getStackTrace();
+
+ for (int i = 0; i < trace.length; i++) {
+ if ("merge".equals(trace[i].getMethodName())) {
+ if (VERBOSE) {
+ System.out.println("TEST: now fail; thread=" + Thread.currentThread().getName() + " exc:");
+ new Throwable().printStackTrace(System.out);
+ }
+ didFail.set(true);
+ throw new FakeIOException();
+ }
+ }
+ }
+ });
+
+ IndexWriterConfig iwc = newIndexWriterConfig();
+ MergeScheduler ms = iwc.getMergeScheduler();
+ if (ms instanceof ConcurrentMergeScheduler) {
+ ((ConcurrentMergeScheduler) ms).setSuppressExceptions();
+ }
+ IndexWriter w = new IndexWriter(dir, iwc);
+
+ while (true) {
+ try {
+ Document doc = new Document();
+ doc.add(newStringField("field", "string", Field.Store.NO));
+ w.addDocument(doc);
+ if (random().nextInt(10) == 7) {
+ // Flush new segment:
+ DirectoryReader.open(w, true).close();
+ }
+ } catch (AlreadyClosedException ace) {
+ // OK: e.g. CMS hit the exc in BG thread and closed the writer
+ break;
+ } catch (FakeIOException fioe) {
+ // OK: e.g. SMS hit the exception
+ break;
+ }
+ }
+
+ assertNotNull(w.getTragicException());
+ assertFalse(w.isOpen());
+ assertTrue(didFail.get());
+
+ if (ms instanceof ConcurrentMergeScheduler) {
+ // Sneaky: CMS's merge thread will be concurrently rolling back IW due
+ // to the tragedy, with this main thread, so we have to wait here
+ // to ensure the rollback has finished, else MDW still sees open files:
+ ((ConcurrentMergeScheduler) ms).sync();
+ }
+
+ dir.close();
+ }
}
Modified: lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterOnDiskFull.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterOnDiskFull.java?rev=1690886&r1=1690885&r2=1690886&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterOnDiskFull.java (original)
+++ lucene/dev/trunk/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterOnDiskFull.java Tue Jul 14 09:38:50 2015
@@ -154,8 +154,7 @@ public class TestIndexWriterOnDiskFull e
either all or none of the incoming documents were in
fact added.
*/
- public void testAddIndexOnDiskFull() throws IOException
- {
+ public void testAddIndexOnDiskFull() throws IOException {
// MemoryCodec, since it uses FST, is not necessarily
// "additive", ie if you add up N small FSTs, then merge
// them, the merged result can easily be larger than the
@@ -256,21 +255,25 @@ public class TestIndexWriterOnDiskFull e
// Make a new dir that will enforce disk usage:
MockDirectoryWrapper dir = new MockDirectoryWrapper(random(), TestUtil.ramCopyOf(startDir));
- writer = new IndexWriter(dir, newIndexWriterConfig(new MockAnalyzer(random()))
- .setOpenMode(OpenMode.APPEND)
- .setMergePolicy(newLogMergePolicy(false)));
- IOException err = null;
+ dir.setPreventDoubleWrite(false);
+ IndexWriterConfig iwc = newIndexWriterConfig(new MockAnalyzer(random()))
+ .setOpenMode(OpenMode.APPEND)
+ .setMergePolicy(newLogMergePolicy(false));
+ writer = new IndexWriter(dir, iwc);
+ Exception err = null;
- MergeScheduler ms = writer.getConfig().getMergeScheduler();
for(int x=0;x<2;x++) {
- if (ms instanceof ConcurrentMergeScheduler)
+ MergeScheduler ms = writer.getConfig().getMergeScheduler();
+ if (ms instanceof ConcurrentMergeScheduler) {
// This test intentionally produces exceptions
// in the threads that CMS launches; we don't
// want to pollute test output with these.
- if (0 == x)
+ if (0 == x) {
((ConcurrentMergeScheduler) ms).setSuppressExceptions();
- else
+ } else {
((ConcurrentMergeScheduler) ms).clearSuppressExceptions();
+ }
+ }
// Two loops: first time, limit disk space &
// throw random IOExceptions; second time, no
@@ -294,18 +297,21 @@ public class TestIndexWriterOnDiskFull e
if (diskRatio >= 6.0) {
rate = 0.0;
}
- if (VERBOSE)
+ if (VERBOSE) {
testName = "disk full test " + methodName + " with disk full at " + diskFree + " bytes";
+ }
} else {
dir.setRandomIOExceptionRateOnOpen(0.0);
thisDiskFree = 0;
rate = 0.0;
- if (VERBOSE)
+ if (VERBOSE) {
testName = "disk full test " + methodName + " with unlimited disk space";
+ }
}
- if (VERBOSE)
+ if (VERBOSE) {
System.out.println("\ncycle: " + testName);
+ }
dir.setTrackDiskUsage(true);
dir.setMaxSizeInBytes(thisDiskFree);
@@ -347,11 +353,11 @@ public class TestIndexWriterOnDiskFull e
done = true;
}
- } catch (IOException e) {
+ } catch (IllegalStateException | IOException e) {
success = false;
err = e;
if (VERBOSE) {
- System.out.println(" hit IOException: " + e);
+ System.out.println(" hit Exception: " + e);
e.printStackTrace(System.out);
}
@@ -361,9 +367,16 @@ public class TestIndexWriterOnDiskFull e
}
}
- // Make sure all threads from
- // ConcurrentMergeScheduler are done
- TestUtil.syncConcurrentMerges(writer);
+ if (x == 1) {
+ // Make sure all threads from ConcurrentMergeScheduler are done
+ TestUtil.syncConcurrentMerges(writer);
+ } else {
+ dir.setRandomIOExceptionRateOnOpen(0.0);
+ writer.rollback();
+ writer = new IndexWriter(dir, newIndexWriterConfig(new MockAnalyzer(random()))
+ .setOpenMode(OpenMode.APPEND)
+ .setMergePolicy(newLogMergePolicy(false)));
+ }
if (VERBOSE) {
System.out.println(" now test readers");
@@ -447,11 +460,6 @@ public class TestIndexWriterOnDiskFull e
writer.close();
- // Wait for all BG threads to finish else
- // dir.close() will throw IOException because
- // there are still open files
- TestUtil.syncConcurrentMerges(ms);
-
dir.close();
// Try again with more free space:
@@ -524,8 +532,12 @@ public class TestIndexWriterOnDiskFull e
}
TestUtil.checkIndex(dir);
ftdm.clearDoFail();
- w.addDocument(doc);
- w.close();
+ try {
+ w.addDocument(doc);
+ fail("writer was not closed by merge exception");
+ } catch (AlreadyClosedException ace) {
+ // expected
+ }
dir.close();
}
@@ -556,20 +568,18 @@ public class TestIndexWriterOnDiskFull e
// TODO: these are also in TestIndexWriter... add a simple doc-writing method
// like this to LuceneTestCase?
- private void addDoc(IndexWriter writer) throws IOException
- {
- Document doc = new Document();
- doc.add(newTextField("content", "aaa", Field.Store.NO));
- doc.add(new NumericDocValuesField("numericdv", 1));
- writer.addDocument(doc);
+ private void addDoc(IndexWriter writer) throws IOException {
+ Document doc = new Document();
+ doc.add(newTextField("content", "aaa", Field.Store.NO));
+ doc.add(new NumericDocValuesField("numericdv", 1));
+ writer.addDocument(doc);
}
- private void addDocWithIndex(IndexWriter writer, int index) throws IOException
- {
- Document doc = new Document();
- doc.add(newTextField("content", "aaa " + index, Field.Store.NO));
- doc.add(newTextField("id", "" + index, Field.Store.NO));
- doc.add(new NumericDocValuesField("numericdv", 1));
- writer.addDocument(doc);
+ private void addDocWithIndex(IndexWriter writer, int index) throws IOException {
+ Document doc = new Document();
+ doc.add(newTextField("content", "aaa " + index, Field.Store.NO));
+ doc.add(newTextField("id", "" + index, Field.Store.NO));
+ doc.add(new NumericDocValuesField("numericdv", 1));
+ writer.addDocument(doc);
}
}
Modified: lucene/dev/trunk/lucene/sandbox/src/test/org/apache/lucene/codecs/idversion/TestIDVersionPostingsFormat.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/sandbox/src/test/org/apache/lucene/codecs/idversion/TestIDVersionPostingsFormat.java?rev=1690886&r1=1690885&r2=1690886&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/sandbox/src/test/org/apache/lucene/codecs/idversion/TestIDVersionPostingsFormat.java (original)
+++ lucene/dev/trunk/lucene/sandbox/src/test/org/apache/lucene/codecs/idversion/TestIDVersionPostingsFormat.java Tue Jul 14 09:38:50 2015
@@ -405,11 +405,11 @@ public class TestIDVersionPostingsFormat
fail("didn't hit exception");
} catch (IllegalArgumentException iae) {
// expected: SMS will hit this
- } catch (IOException ioe) {
+ } catch (IOException | IllegalStateException exc) {
// expected
- assertTrue(ioe.getCause() instanceof IllegalArgumentException);
+ assertTrue(exc.getCause() instanceof IllegalArgumentException);
}
- w.close();
+ w.rollback();
dir.close();
}