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 2014/09/19 14:03:46 UTC
svn commit: r1626187 - in /lucene/dev/branches/lucene_solr_4_10: ./ lucene/
lucene/core/ lucene/core/src/java/org/apache/lucene/index/
lucene/core/src/test/org/apache/lucene/index/
Author: rmuir
Date: Fri Sep 19 12:03:46 2014
New Revision: 1626187
URL: http://svn.apache.org/r1626187
Log:
LUCENE-5919: fix IndexWriter exception handling when calling IndexFileDeleter.decRef
Modified:
lucene/dev/branches/lucene_solr_4_10/ (props changed)
lucene/dev/branches/lucene_solr_4_10/lucene/ (props changed)
lucene/dev/branches/lucene_solr_4_10/lucene/CHANGES.txt
lucene/dev/branches/lucene_solr_4_10/lucene/core/ (props changed)
lucene/dev/branches/lucene_solr_4_10/lucene/core/src/java/org/apache/lucene/index/IndexFileDeleter.java
lucene/dev/branches/lucene_solr_4_10/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java
lucene/dev/branches/lucene_solr_4_10/lucene/core/src/test/org/apache/lucene/index/TestIndexFileDeleter.java
Modified: lucene/dev/branches/lucene_solr_4_10/lucene/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene_solr_4_10/lucene/CHANGES.txt?rev=1626187&r1=1626186&r2=1626187&view=diff
==============================================================================
--- lucene/dev/branches/lucene_solr_4_10/lucene/CHANGES.txt (original)
+++ lucene/dev/branches/lucene_solr_4_10/lucene/CHANGES.txt Fri Sep 19 12:03:46 2014
@@ -20,6 +20,11 @@ Bug fixes
1.8.0_20 JVM bug in String.split. (Ryan Ernst, Uwe Schindler,
Robert Muir, Mike McCandless)
+* LUCENE-5919: Fix exception handling inside IndexWriter when
+ deleteFile throws an exception, to not over-decRef index files,
+ possibly deleting a file that's still in use in the index, leading
+ to corruption. (Mike McCandless)
+
Tests
* LUCENE-5936: Add backcompat checks to verify what is tested matches known versions
Modified: lucene/dev/branches/lucene_solr_4_10/lucene/core/src/java/org/apache/lucene/index/IndexFileDeleter.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene_solr_4_10/lucene/core/src/java/org/apache/lucene/index/IndexFileDeleter.java?rev=1626187&r1=1626186&r2=1626187&view=diff
==============================================================================
--- lucene/dev/branches/lucene_solr_4_10/lucene/core/src/java/org/apache/lucene/index/IndexFileDeleter.java (original)
+++ lucene/dev/branches/lucene_solr_4_10/lucene/core/src/java/org/apache/lucene/index/IndexFileDeleter.java Fri Sep 19 12:03:46 2014
@@ -33,6 +33,7 @@ import org.apache.lucene.store.AlreadyCl
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.NoSuchDirectoryException;
import org.apache.lucene.util.CollectionUtil;
+import org.apache.lucene.util.IOUtils;
import org.apache.lucene.util.InfoStream;
/*
@@ -287,17 +288,25 @@ final class IndexFileDeleter implements
// First decref all files that had been referred to by
// the now-deleted commits:
+ Throwable firstThrowable = null;
for(int i=0;i<size;i++) {
CommitPoint commit = commitsToDelete.get(i);
if (infoStream.isEnabled("IFD")) {
infoStream.message("IFD", "deleteCommits: now decRef commit \"" + commit.getSegmentsFileName() + "\"");
}
- for (final String file : commit.files) {
- decRef(file);
+ try {
+ decRef(commit.files);
+ } catch (Throwable t) {
+ if (firstThrowable == null) {
+ firstThrowable = t;
+ }
}
}
commitsToDelete.clear();
+ // NOTE: does nothing if firstThrowable is null
+ IOUtils.reThrow(firstThrowable);
+
// Now compact commits to remove deleted ones (preserving the sort):
size = commits.size();
int readFrom = 0;
@@ -376,8 +385,11 @@ final class IndexFileDeleter implements
assert locked();
if (!lastFiles.isEmpty()) {
- decRef(lastFiles);
- lastFiles.clear();
+ try {
+ decRef(lastFiles);
+ } finally {
+ lastFiles.clear();
+ }
}
deletePendingFiles();
@@ -467,8 +479,11 @@ final class IndexFileDeleter implements
deleteCommits();
} else {
// DecRef old files from the last checkpoint, if any:
- decRef(lastFiles);
- lastFiles.clear();
+ try {
+ decRef(lastFiles);
+ } finally {
+ lastFiles.clear();
+ }
// Save files so we can decr on next checkpoint/commit:
lastFiles.addAll(segmentInfos.files(directory, false));
@@ -506,10 +521,34 @@ final class IndexFileDeleter implements
rc.IncRef();
}
+ /** Decrefs all provided files, even on exception; throws first exception hit, if any. */
void decRef(Collection<String> files) throws IOException {
assert locked();
+ Throwable firstThrowable = null;
for(final String file : files) {
- decRef(file);
+ try {
+ decRef(file);
+ } catch (Throwable t) {
+ if (firstThrowable == null) {
+ // Save first exception and throw it in the end, but be sure to finish decRef all files
+ firstThrowable = t;
+ }
+ }
+ }
+
+ // NOTE: does nothing if firstThrowable is null
+ IOUtils.reThrow(firstThrowable);
+ }
+
+ /** Decrefs all provided files, ignoring any exceptions hit; call this if
+ * you are already handling an exception. */
+ void decRefWhileHandlingException(Collection<String> files) throws IOException {
+ assert locked();
+ for(final String file : files) {
+ try {
+ decRef(file);
+ } catch (Throwable t) {
+ }
}
}
@@ -524,16 +563,17 @@ final class IndexFileDeleter implements
if (0 == rc.DecRef()) {
// This file is no longer referenced by any past
// commit points nor by the in-memory SegmentInfos:
- deleteFile(fileName);
- refCounts.remove(fileName);
+ try {
+ deleteFile(fileName);
+ } finally {
+ refCounts.remove(fileName);
+ }
}
}
void decRef(SegmentInfos segmentInfos) throws IOException {
assert locked();
- for (final String file : segmentInfos.files(directory, false)) {
- decRef(file);
- }
+ decRef(segmentInfos.files(directory, false));
}
public boolean exists(String fileName) {
Modified: lucene/dev/branches/lucene_solr_4_10/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene_solr_4_10/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java?rev=1626187&r1=1626186&r2=1626187&view=diff
==============================================================================
--- lucene/dev/branches/lucene_solr_4_10/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java (original)
+++ lucene/dev/branches/lucene_solr_4_10/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java Fri Sep 19 12:03:46 2014
@@ -2170,9 +2170,12 @@ public class IndexWriter implements Clos
if (pendingCommit != null) {
pendingCommit.rollbackCommit(directory);
- deleter.decRef(pendingCommit);
- pendingCommit = null;
- notifyAll();
+ try {
+ deleter.decRef(pendingCommit);
+ } finally {
+ pendingCommit = null;
+ notifyAll();
+ }
}
// Don't bother saving any changes in our segmentInfos
@@ -2225,7 +2228,9 @@ public class IndexWriter implements Clos
try {
pendingCommit.rollbackCommit(directory);
deleter.decRef(pendingCommit);
- } catch (Throwable t) {}
+ } catch (Throwable t) {
+ }
+ pendingCommit = null;
}
// close all the closeables we can (but important is readerPool and writeLock to prevent leaks)
@@ -3065,7 +3070,7 @@ public class IndexWriter implements Clos
if (!success) {
synchronized (this) {
if (filesToCommit != null) {
- deleter.decRef(filesToCommit);
+ deleter.decRefWhileHandlingException(filesToCommit);
filesToCommit = null;
}
}
@@ -3196,10 +3201,13 @@ public class IndexWriter implements Clos
deleter.checkpoint(pendingCommit, true);
} finally {
// Matches the incRef done in prepareCommit:
- deleter.decRef(filesToCommit);
- filesToCommit = null;
- pendingCommit = null;
- notifyAll();
+ try {
+ deleter.decRef(filesToCommit);
+ } finally {
+ filesToCommit = null;
+ pendingCommit = null;
+ notifyAll();
+ }
}
if (infoStream.isEnabled("IW")) {
@@ -4517,8 +4525,11 @@ public class IndexWriter implements Clos
if (infoStream.isEnabled("IW")) {
infoStream.message("IW", " skip startCommit(): no changes pending");
}
- deleter.decRef(filesToCommit);
- filesToCommit = null;
+ try {
+ deleter.decRef(filesToCommit);
+ } finally {
+ filesToCommit = null;
+ }
return;
}
@@ -4589,7 +4600,7 @@ public class IndexWriter implements Clos
}
// Hit exception
- deleter.decRef(filesToCommit);
+ deleter.decRefWhileHandlingException(filesToCommit);
filesToCommit = null;
}
}
Modified: lucene/dev/branches/lucene_solr_4_10/lucene/core/src/test/org/apache/lucene/index/TestIndexFileDeleter.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene_solr_4_10/lucene/core/src/test/org/apache/lucene/index/TestIndexFileDeleter.java?rev=1626187&r1=1626186&r2=1626187&view=diff
==============================================================================
--- lucene/dev/branches/lucene_solr_4_10/lucene/core/src/test/org/apache/lucene/index/TestIndexFileDeleter.java (original)
+++ lucene/dev/branches/lucene_solr_4_10/lucene/core/src/test/org/apache/lucene/index/TestIndexFileDeleter.java Fri Sep 19 12:03:46 2014
@@ -19,6 +19,7 @@ package org.apache.lucene.index;
import java.io.*;
import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.lucene.analysis.MockAnalyzer;
import org.apache.lucene.codecs.Codec;
@@ -203,4 +204,95 @@ public class TestIndexFileDeleter extend
doc.add(newStringField("id", Integer.toString(id), Field.Store.NO));
writer.addDocument(doc);
}
+
+ // LUCENE-5919
+ public void testExcInDecRef() throws Exception {
+ MockDirectoryWrapper dir = newMockDirectory();
+
+ // disable slow things: we don't rely upon sleeps here.
+ dir.setThrottling(MockDirectoryWrapper.Throttling.NEVER);
+ dir.setUseSlowOpenClosers(false);
+
+ final AtomicBoolean doFailExc = new AtomicBoolean();
+
+ dir.failOn(new MockDirectoryWrapper.Failure() {
+ @Override
+ public void eval(MockDirectoryWrapper dir) throws IOException {
+ if (doFailExc.get() && random().nextInt(4) == 1) {
+ Exception e = new Exception();
+ StackTraceElement stack[] = e.getStackTrace();
+ for (int i = 0; i < stack.length; i++) {
+ if (stack[i].getClassName().equals(IndexFileDeleter.class.getName()) && stack[i].getMethodName().equals("decRef")) {
+ throw new RuntimeException("fake fail");
+ }
+ }
+ }
+ }
+ });
+
+ IndexWriterConfig iwc = newIndexWriterConfig(new MockAnalyzer(random()));
+ //iwc.setMergeScheduler(new SerialMergeScheduler());
+ MergeScheduler ms = iwc.getMergeScheduler();
+ if (ms instanceof ConcurrentMergeScheduler) {
+ final ConcurrentMergeScheduler suppressFakeFail = new ConcurrentMergeScheduler() {
+ @Override
+ protected void handleMergeException(Throwable exc) {
+ // suppress only FakeIOException:
+ if (exc instanceof RuntimeException && exc.getMessage().equals("fake fail")) {
+ // ok to ignore
+ } else {
+ super.handleMergeException(exc);
+ }
+ }
+ };
+ final ConcurrentMergeScheduler cms = (ConcurrentMergeScheduler) ms;
+ suppressFakeFail.setMaxMergesAndThreads(cms.getMaxMergeCount(), cms.getMaxThreadCount());
+ suppressFakeFail.setMergeThreadPriority(cms.getMergeThreadPriority());
+ iwc.setMergeScheduler(suppressFakeFail);
+ }
+
+ RandomIndexWriter w = new RandomIndexWriter(random(), dir, iwc);
+
+ // Since we hit exc during merging, a partial
+ // forceMerge can easily return when there are still
+ // too many segments in the index:
+ w.setDoRandomForceMergeAssert(false);
+
+ doFailExc.set(true);
+ int ITERS = atLeast(1000);
+ for(int iter=0;iter<ITERS;iter++) {
+ try {
+ if (random().nextInt(10) == 5) {
+ w.commit();
+ } else if (random().nextInt(10) == 7) {
+ w.getReader().close();
+ } else {
+ Document doc = new Document();
+ 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")) {
+ // ok
+ } else {
+ throw re;
+ }
+ }
+ }
+
+ doFailExc.set(false);
+ w.close();
+ dir.close();
+ }
}