add test case to ensure SearchManager can handle concurrent close, refresh, searching and IndexWriter closing

This commit is contained in:
Mike McCandless 2016-08-19 09:28:17 -04:00
parent 3e64a97597
commit f0963b3b4f
2 changed files with 143 additions and 1 deletions

View File

@ -26,6 +26,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.lucene.analysis.MockAnalyzer;
import org.apache.lucene.document.Document;
@ -43,6 +44,7 @@ import org.apache.lucene.index.Term;
import org.apache.lucene.index.ThreadedIndexingAndSearchingTestCase;
import org.apache.lucene.store.AlreadyClosedException;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.LineFileDocs;
import org.apache.lucene.util.LuceneTestCase.SuppressCodecs;
import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.util.NamedThreadFactory;
@ -533,4 +535,144 @@ public class TestSearcherManager extends ThreadedIndexingAndSearchingTestCase {
sm.close();
dir.close();
}
public void testConcurrentIndexCloseSearchAndRefresh() throws Exception {
final Directory dir = newFSDirectory(createTempDir());
AtomicReference<IndexWriter> writerRef = new AtomicReference<>();
writerRef.set(new IndexWriter(dir, newIndexWriterConfig()));
AtomicReference<SearcherManager> mgrRef = new AtomicReference<>();
mgrRef.set(new SearcherManager(writerRef.get(), null));
final AtomicBoolean stop = new AtomicBoolean();
Thread indexThread = new Thread() {
@Override
public void run() {
try {
LineFileDocs docs = new LineFileDocs(random());
long runTimeSec = TEST_NIGHTLY ? atLeast(10) : atLeast(2);
long endTime = System.nanoTime() + runTimeSec * 1000000000;
while (System.nanoTime() < endTime) {
IndexWriter w = writerRef.get();
w.addDocument(docs.nextDoc());
if (random().nextInt(1000) == 17) {
if (random().nextBoolean()) {
w.close();
} else {
w.rollback();
}
writerRef.set(new IndexWriter(dir, newIndexWriterConfig()));
}
}
docs.close();
stop.set(true);
if (VERBOSE) {
System.out.println("TEST: index count=" + writerRef.get().maxDoc());
}
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
};
Thread searchThread = new Thread() {
@Override
public void run() {
try {
long totCount = 0;
while (stop.get() == false) {
SearcherManager mgr = mgrRef.get();
if (mgr != null) {
IndexSearcher searcher;
try {
searcher = mgr.acquire();
} catch (AlreadyClosedException ace) {
// ok
continue;
}
totCount += searcher.getIndexReader().maxDoc();
mgr.release(searcher);
}
}
if (VERBOSE) {
System.out.println("TEST: search totCount=" + totCount);
}
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
};
Thread refreshThread = new Thread() {
@Override
public void run() {
try {
int refreshCount = 0;
int aceCount = 0;
while (stop.get() == false) {
SearcherManager mgr = mgrRef.get();
if (mgr != null) {
refreshCount++;
try {
mgr.maybeRefreshBlocking();
} catch (AlreadyClosedException ace) {
// ok
aceCount++;
continue;
}
}
}
if (VERBOSE) {
System.out.println("TEST: refresh count=" + refreshCount + " aceCount=" + aceCount);
}
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
};
Thread closeThread = new Thread() {
@Override
public void run() {
try {
int closeCount = 0;
int aceCount = 0;
while (stop.get() == false) {
SearcherManager mgr = mgrRef.get();
assert mgr != null;
mgr.close();
closeCount++;
while (stop.get() == false) {
try {
mgrRef.set(new SearcherManager(writerRef.get(), null));
break;
} catch (AlreadyClosedException ace) {
// ok
aceCount++;
}
}
}
if (VERBOSE) {
System.out.println("TEST: close count=" + closeCount + " aceCount=" + aceCount);
}
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
};
indexThread.start();
searchThread.start();
refreshThread.start();
closeThread.start();
indexThread.join();
searchThread.join();
refreshThread.join();
closeThread.join();
mgrRef.get().close();
writerRef.get().close();
dir.close();
}
}

View File

@ -838,7 +838,7 @@ public class MockDirectoryWrapper extends BaseDirectoryWrapper {
}
// RuntimeException instead of IOException because
// super() does not throw IOException currently:
throw new RuntimeException("MockDirectoryWrapper: cannot close: there are still open files: " + openFiles, cause);
throw new RuntimeException("MockDirectoryWrapper: cannot close: there are still " + openFiles.size() + " open files: " + openFiles, cause);
}
if (openLocks.size() > 0) {
Exception cause = null;