mirror of https://github.com/apache/lucene.git
LUCENE-5574: when closing an NRT reader, do not delete files if the original writer has since been closed
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1584840 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
f49524fb57
commit
a858257fc6
|
@ -236,6 +236,13 @@ Bug fixes
|
||||||
|
|
||||||
* LUCENE-5568: Benchmark module's "default.codec" option didn't work. (David Smiley)
|
* LUCENE-5568: Benchmark module's "default.codec" option didn't work. (David Smiley)
|
||||||
|
|
||||||
|
* LUCENE-5574: Closing a near-real-time reader no longer attempts to
|
||||||
|
delete unreferenced files if the original writer has been closed;
|
||||||
|
this could cause index corruption in certain cases where index files
|
||||||
|
were directly changed (deleted, overwritten, etc.) in the index
|
||||||
|
directory outside of Lucene. (Simon Willnauer, Shai Erera, Robert
|
||||||
|
Muir, Mike McCandless)
|
||||||
|
|
||||||
Test Framework
|
Test Framework
|
||||||
|
|
||||||
* LUCENE-5567: When a suite fails with zombie threads failure marker and count
|
* LUCENE-5567: When a suite fails with zombie threads failure marker and count
|
||||||
|
|
|
@ -29,6 +29,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
|
import org.apache.lucene.store.AlreadyClosedException;
|
||||||
import org.apache.lucene.store.Directory;
|
import org.apache.lucene.store.Directory;
|
||||||
import org.apache.lucene.store.NoSuchDirectoryException;
|
import org.apache.lucene.store.NoSuchDirectoryException;
|
||||||
import org.apache.lucene.util.CollectionUtil;
|
import org.apache.lucene.util.CollectionUtil;
|
||||||
|
@ -262,6 +263,14 @@ final class IndexFileDeleter implements Closeable {
|
||||||
deleteCommits();
|
deleteCommits();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ensureOpen() throws AlreadyClosedException {
|
||||||
|
if (writer == null) {
|
||||||
|
throw new AlreadyClosedException("this IndexWriter is closed");
|
||||||
|
} else {
|
||||||
|
writer.ensureOpen(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public SegmentInfos getLastSegmentInfos() {
|
public SegmentInfos getLastSegmentInfos() {
|
||||||
return lastSegmentInfos;
|
return lastSegmentInfos;
|
||||||
}
|
}
|
||||||
|
@ -578,6 +587,7 @@ final class IndexFileDeleter implements Closeable {
|
||||||
void deleteFile(String fileName)
|
void deleteFile(String fileName)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
assert locked();
|
assert locked();
|
||||||
|
ensureOpen();
|
||||||
try {
|
try {
|
||||||
if (infoStream.isEnabled("IFD")) {
|
if (infoStream.isEnabled("IFD")) {
|
||||||
infoStream.message("IFD", "delete \"" + fileName + "\"");
|
infoStream.message("IFD", "delete \"" + fileName + "\"");
|
||||||
|
|
|
@ -4566,8 +4566,7 @@ public class IndexWriter implements Closeable, TwoPhaseCommit{
|
||||||
deleter.revisitPolicy();
|
deleter.revisitPolicy();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called by DirectoryReader.doClose
|
private synchronized void deletePendingFiles() throws IOException {
|
||||||
synchronized void deletePendingFiles() throws IOException {
|
|
||||||
deleter.deletePendingFiles();
|
deleter.deletePendingFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4665,10 +4664,12 @@ public class IndexWriter implements Closeable, TwoPhaseCommit{
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void incRefDeleter(SegmentInfos segmentInfos) throws IOException {
|
synchronized void incRefDeleter(SegmentInfos segmentInfos) throws IOException {
|
||||||
|
ensureOpen();
|
||||||
deleter.incRef(segmentInfos, false);
|
deleter.incRef(segmentInfos, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void decRefDeleter(SegmentInfos segmentInfos) throws IOException {
|
synchronized void decRefDeleter(SegmentInfos segmentInfos) throws IOException {
|
||||||
|
ensureOpen();
|
||||||
deleter.decRef(segmentInfos);
|
deleter.decRef(segmentInfos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.lucene.store.AlreadyClosedException;
|
||||||
import org.apache.lucene.store.Directory;
|
import org.apache.lucene.store.Directory;
|
||||||
import org.apache.lucene.store.IOContext;
|
import org.apache.lucene.store.IOContext;
|
||||||
import org.apache.lucene.util.IOUtils;
|
import org.apache.lucene.util.IOUtils;
|
||||||
|
@ -365,11 +366,15 @@ final class StandardDirectoryReader extends DirectoryReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (writer != null) {
|
if (writer != null) {
|
||||||
writer.decRefDeleter(segmentInfos);
|
try {
|
||||||
|
writer.decRefDeleter(segmentInfos);
|
||||||
// Since we just closed, writer may now be able to
|
} catch (AlreadyClosedException ex) {
|
||||||
// delete unused files:
|
// This is OK, it just means our original writer was
|
||||||
writer.deletePendingFiles();
|
// closed before we were, and this may leave some
|
||||||
|
// un-referenced files in the index, which is
|
||||||
|
// harmless. The next time IW is opened on the
|
||||||
|
// index, it will delete them.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// throw the first exception
|
// throw the first exception
|
||||||
|
|
|
@ -2371,4 +2371,42 @@ public class TestIndexWriter extends LuceneTestCase {
|
||||||
r.close();
|
r.close();
|
||||||
dir.close();
|
dir.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LUCENE-5574
|
||||||
|
public void testClosingNRTReaderDoesNotCorruptYourIndex() throws IOException {
|
||||||
|
MockDirectoryWrapper dir = newMockDirectory();
|
||||||
|
|
||||||
|
// Allow deletion of still open files:
|
||||||
|
dir.setNoDeleteOpenFile(false);
|
||||||
|
|
||||||
|
// Allow writing to same file more than once:
|
||||||
|
dir.setPreventDoubleWrite(false);
|
||||||
|
|
||||||
|
IndexWriterConfig iwc = newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random()));
|
||||||
|
LogMergePolicy lmp = new LogDocMergePolicy();
|
||||||
|
lmp.setMergeFactor(2);
|
||||||
|
iwc.setMergePolicy(lmp);
|
||||||
|
|
||||||
|
RandomIndexWriter w = new RandomIndexWriter(random(), dir, iwc);
|
||||||
|
Document doc = new Document();
|
||||||
|
doc.add(new TextField("a", "foo", Field.Store.NO));
|
||||||
|
w.addDocument(doc);
|
||||||
|
w.commit();
|
||||||
|
w.addDocument(doc);
|
||||||
|
|
||||||
|
// Get a new reader, but this also sets off a merge:
|
||||||
|
IndexReader r = w.getReader();
|
||||||
|
w.close();
|
||||||
|
|
||||||
|
// Blow away index and make a new writer:
|
||||||
|
for(String fileName : dir.listAll()) {
|
||||||
|
dir.deleteFile(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
w = new RandomIndexWriter(random(), dir);
|
||||||
|
w.addDocument(doc);
|
||||||
|
w.close();
|
||||||
|
r.close();
|
||||||
|
dir.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,15 +22,14 @@ import java.util.Collections;
|
||||||
import org.apache.lucene.document.Document;
|
import org.apache.lucene.document.Document;
|
||||||
import org.apache.lucene.index.IndexWriter;
|
import org.apache.lucene.index.IndexWriter;
|
||||||
import org.apache.lucene.index.IndexWriterConfig;
|
import org.apache.lucene.index.IndexWriterConfig;
|
||||||
import org.apache.lucene.store.Directory;
|
|
||||||
import org.apache.lucene.store.IOContext;
|
import org.apache.lucene.store.IOContext;
|
||||||
import org.apache.lucene.store.IndexOutput;
|
import org.apache.lucene.store.IndexOutput;
|
||||||
|
import org.apache.lucene.store.MockDirectoryWrapper;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.JUnitCore;
|
import org.junit.runner.JUnitCore;
|
||||||
import org.junit.runner.Result;
|
import org.junit.runner.Result;
|
||||||
import org.junit.runner.notification.Failure;
|
import org.junit.runner.notification.Failure;
|
||||||
|
|
||||||
import com.carrotsearch.randomizedtesting.RandomizedTest;
|
import com.carrotsearch.randomizedtesting.RandomizedTest;
|
||||||
|
|
||||||
// LUCENE-4456: Test that we fail if there are unreferenced files
|
// LUCENE-4456: Test that we fail if there are unreferenced files
|
||||||
|
@ -41,7 +40,8 @@ public class TestFailIfUnreferencedFiles extends WithNestedTests {
|
||||||
|
|
||||||
public static class Nested1 extends WithNestedTests.AbstractNestedTest {
|
public static class Nested1 extends WithNestedTests.AbstractNestedTest {
|
||||||
public void testDummy() throws Exception {
|
public void testDummy() throws Exception {
|
||||||
Directory dir = newMockDirectory();
|
MockDirectoryWrapper dir = newMockDirectory();
|
||||||
|
dir.setAssertNoUnrefencedFilesOnClose(true);
|
||||||
IndexWriter iw = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, null));
|
IndexWriter iw = new IndexWriter(dir, new IndexWriterConfig(TEST_VERSION_CURRENT, null));
|
||||||
iw.addDocument(new Document());
|
iw.addDocument(new Document());
|
||||||
iw.close();
|
iw.close();
|
||||||
|
|
|
@ -622,7 +622,8 @@ public class MockDirectoryWrapper extends BaseDirectoryWrapper {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean assertNoUnreferencedFilesOnClose = true;
|
// NOTE: This is off by default; see LUCENE-5574
|
||||||
|
private boolean assertNoUnreferencedFilesOnClose;
|
||||||
|
|
||||||
public void setAssertNoUnrefencedFilesOnClose(boolean v) {
|
public void setAssertNoUnrefencedFilesOnClose(boolean v) {
|
||||||
assertNoUnreferencedFilesOnClose = v;
|
assertNoUnreferencedFilesOnClose = v;
|
||||||
|
|
Loading…
Reference in New Issue