mirror of https://github.com/apache/lucene.git
add test case to ensure SearchManager can handle concurrent close, refresh, searching and IndexWriter closing
This commit is contained in:
parent
3e64a97597
commit
f0963b3b4f
|
@ -26,6 +26,7 @@ import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import org.apache.lucene.analysis.MockAnalyzer;
|
import org.apache.lucene.analysis.MockAnalyzer;
|
||||||
import org.apache.lucene.document.Document;
|
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.index.ThreadedIndexingAndSearchingTestCase;
|
||||||
import org.apache.lucene.store.AlreadyClosedException;
|
import org.apache.lucene.store.AlreadyClosedException;
|
||||||
import org.apache.lucene.store.Directory;
|
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.SuppressCodecs;
|
||||||
import org.apache.lucene.util.LuceneTestCase;
|
import org.apache.lucene.util.LuceneTestCase;
|
||||||
import org.apache.lucene.util.NamedThreadFactory;
|
import org.apache.lucene.util.NamedThreadFactory;
|
||||||
|
@ -533,4 +535,144 @@ public class TestSearcherManager extends ThreadedIndexingAndSearchingTestCase {
|
||||||
sm.close();
|
sm.close();
|
||||||
dir.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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -838,7 +838,7 @@ public class MockDirectoryWrapper extends BaseDirectoryWrapper {
|
||||||
}
|
}
|
||||||
// RuntimeException instead of IOException because
|
// RuntimeException instead of IOException because
|
||||||
// super() does not throw IOException currently:
|
// 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) {
|
if (openLocks.size() > 0) {
|
||||||
Exception cause = null;
|
Exception cause = null;
|
||||||
|
|
Loading…
Reference in New Issue