|
@@ -61,6 +61,7 @@ import org.apache.lucene.search.TopDocs;
|
|
|
import org.apache.lucene.search.TotalHitCountCollector;
|
|
|
import org.apache.lucene.store.AlreadyClosedException;
|
|
|
import org.apache.lucene.store.Directory;
|
|
|
+import org.apache.lucene.store.Lock;
|
|
|
import org.apache.lucene.store.MockDirectoryWrapper;
|
|
|
import org.apache.lucene.util.Bits;
|
|
|
import org.apache.lucene.util.BytesRef;
|
|
@@ -91,6 +92,7 @@ import org.elasticsearch.common.lucene.uid.VersionsAndSeqNoResolver.DocIdAndSeqN
|
|
|
import org.elasticsearch.common.settings.Settings;
|
|
|
import org.elasticsearch.common.unit.TimeValue;
|
|
|
import org.elasticsearch.common.util.BigArrays;
|
|
|
+import org.elasticsearch.common.util.concurrent.AbstractRunnable;
|
|
|
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
|
|
|
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
|
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
|
@@ -2926,6 +2928,60 @@ public class InternalEngineTests extends ESTestCase {
|
|
|
assertTrue("expected an Exception that signals shard is not available", TransportActions.isShardNotAvailableException(exception.get()));
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Tests that when the the close method returns the engine is actually guaranteed to have cleaned up and that resources are closed
|
|
|
+ */
|
|
|
+ public void testConcurrentEngineClosed() throws BrokenBarrierException, InterruptedException {
|
|
|
+ Thread[] closingThreads = new Thread[3];
|
|
|
+ CyclicBarrier barrier = new CyclicBarrier(1 + closingThreads.length + 1);
|
|
|
+ Thread failEngine = new Thread(new AbstractRunnable() {
|
|
|
+ @Override
|
|
|
+ public void onFailure(Exception e) {
|
|
|
+ throw new AssertionError(e);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected void doRun() throws Exception {
|
|
|
+ barrier.await();
|
|
|
+ engine.failEngine("test", new RuntimeException("test"));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ failEngine.start();
|
|
|
+ for (int i = 0;i < closingThreads.length ; i++) {
|
|
|
+ boolean flushAndClose = randomBoolean();
|
|
|
+ closingThreads[i] = new Thread(new AbstractRunnable() {
|
|
|
+ @Override
|
|
|
+ public void onFailure(Exception e) {
|
|
|
+ throw new AssertionError(e);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected void doRun() throws Exception {
|
|
|
+ barrier.await();
|
|
|
+ if (flushAndClose) {
|
|
|
+ engine.flushAndClose();
|
|
|
+ } else {
|
|
|
+ engine.close();
|
|
|
+ }
|
|
|
+ // try to acquire the writer lock - i.e., everything is closed, we need to synchronize
|
|
|
+ // to avoid races between closing threads
|
|
|
+ synchronized (closingThreads) {
|
|
|
+ try (Lock ignored = store.directory().obtainLock(IndexWriter.WRITE_LOCK_NAME)) {
|
|
|
+ // all good.
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ closingThreads[i].setName("closingThread_" + i);
|
|
|
+ closingThreads[i].start();
|
|
|
+ }
|
|
|
+ barrier.await();
|
|
|
+ failEngine.join();
|
|
|
+ for (Thread t : closingThreads) {
|
|
|
+ t.join();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
public void testCurrentTranslogIDisCommitted() throws IOException {
|
|
|
try (Store store = createStore()) {
|
|
|
EngineConfig config = config(defaultSettings, store, createTempDir(), newMergePolicy(), null);
|