|
|
@@ -37,7 +37,6 @@ import org.elasticsearch.index.engine.EngineConfig;
|
|
|
import org.elasticsearch.index.engine.EngineException;
|
|
|
import org.elasticsearch.index.shard.ShardId;
|
|
|
import org.elasticsearch.test.ESIntegTestCase;
|
|
|
-import org.elasticsearch.test.engine.MockInternalEngine;
|
|
|
|
|
|
import java.io.Closeable;
|
|
|
import java.io.IOException;
|
|
|
@@ -71,7 +70,7 @@ public final class MockEngineSupport {
|
|
|
private final ShardId shardId;
|
|
|
private final QueryCache filterCache;
|
|
|
private final QueryCachingPolicy filterCachingPolicy;
|
|
|
- private final SearcherCloseable searcherCloseable;
|
|
|
+ private final InFlightSearchers inFlightSearchers;
|
|
|
private final MockContext mockContext;
|
|
|
private final boolean disableFlushOnClose;
|
|
|
|
|
|
@@ -107,8 +106,8 @@ public final class MockEngineSupport {
|
|
|
logger.trace("Using [{}] for shard [{}] seed: [{}] wrapReader: [{}]", this.getClass().getName(), shardId, seed, wrapReader);
|
|
|
}
|
|
|
mockContext = new MockContext(random, wrapReader, wrapper, settings);
|
|
|
- this.searcherCloseable = new SearcherCloseable();
|
|
|
- LuceneTestCase.closeAfterSuite(searcherCloseable); // only one suite closeable per Engine
|
|
|
+ this.inFlightSearchers = new InFlightSearchers();
|
|
|
+ LuceneTestCase.closeAfterSuite(inFlightSearchers); // only one suite closeable per Engine
|
|
|
this.disableFlushOnClose = DISABLE_FLUSH_ON_CLOSE.get(settings);
|
|
|
}
|
|
|
|
|
|
@@ -188,7 +187,7 @@ public final class MockEngineSupport {
|
|
|
|
|
|
}
|
|
|
|
|
|
- public Engine.Searcher wrapSearcher(String source, Engine.Searcher engineSearcher) {
|
|
|
+ public Engine.Searcher wrapSearcher(Engine.Searcher engineSearcher) {
|
|
|
final AssertingIndexSearcher assertingIndexSearcher = newSearcher(engineSearcher);
|
|
|
assertingIndexSearcher.setSimilarity(engineSearcher.searcher().getSimilarity());
|
|
|
/*
|
|
|
@@ -199,26 +198,16 @@ public final class MockEngineSupport {
|
|
|
* early. - good news, stuff will fail all over the place if we don't
|
|
|
* get this right here
|
|
|
*/
|
|
|
- AssertingSearcher assertingSearcher = new AssertingSearcher(assertingIndexSearcher, engineSearcher, shardId, logger) {
|
|
|
- @Override
|
|
|
- public void close() {
|
|
|
- try {
|
|
|
- searcherCloseable.remove(this);
|
|
|
- } finally {
|
|
|
- super.close();
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
- searcherCloseable.add(assertingSearcher, engineSearcher.source());
|
|
|
- return assertingSearcher;
|
|
|
+ SearcherCloseable closeable = new SearcherCloseable(engineSearcher, logger, inFlightSearchers);
|
|
|
+ return new Engine.Searcher(engineSearcher.source(), assertingIndexSearcher, closeable);
|
|
|
}
|
|
|
|
|
|
- private static final class SearcherCloseable implements Closeable {
|
|
|
+ private static final class InFlightSearchers implements Closeable {
|
|
|
|
|
|
- private final IdentityHashMap<AssertingSearcher, RuntimeException> openSearchers = new IdentityHashMap<>();
|
|
|
+ private final IdentityHashMap<Object, RuntimeException> openSearchers = new IdentityHashMap<>();
|
|
|
|
|
|
@Override
|
|
|
- public synchronized void close() throws IOException {
|
|
|
+ public synchronized void close() {
|
|
|
if (openSearchers.isEmpty() == false) {
|
|
|
AssertionError error = new AssertionError("Unreleased searchers found");
|
|
|
for (RuntimeException ex : openSearchers.values()) {
|
|
|
@@ -228,15 +217,66 @@ public final class MockEngineSupport {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- void add(AssertingSearcher searcher, String source) {
|
|
|
+ void add(Object key, String source) {
|
|
|
final RuntimeException ex = new RuntimeException("Unreleased Searcher, source [" + source+ "]");
|
|
|
synchronized (this) {
|
|
|
- openSearchers.put(searcher, ex);
|
|
|
+ openSearchers.put(key, ex);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- synchronized void remove(AssertingSearcher searcher) {
|
|
|
- openSearchers.remove(searcher);
|
|
|
+ synchronized void remove(Object key) {
|
|
|
+ openSearchers.remove(key);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class SearcherCloseable implements Closeable {
|
|
|
+ private final Engine.Searcher wrappedSearcher;
|
|
|
+ private final InFlightSearchers inFlightSearchers;
|
|
|
+ private RuntimeException firstReleaseStack;
|
|
|
+ private final Object lock = new Object();
|
|
|
+ private final int initialRefCount;
|
|
|
+ private final Logger logger;
|
|
|
+ private final AtomicBoolean closed = new AtomicBoolean(false);
|
|
|
+
|
|
|
+ SearcherCloseable(final Engine.Searcher wrappedSearcher, Logger logger, InFlightSearchers inFlightSearchers) {
|
|
|
+ // we only use the given index searcher here instead of the IS of the wrapped searcher. the IS might be a wrapped searcher
|
|
|
+ // with a wrapped reader.
|
|
|
+ this.wrappedSearcher = wrappedSearcher;
|
|
|
+ this.logger = logger;
|
|
|
+ initialRefCount = wrappedSearcher.reader().getRefCount();
|
|
|
+ this.inFlightSearchers = inFlightSearchers;
|
|
|
+ assert initialRefCount > 0 :
|
|
|
+ "IndexReader#getRefCount() was [" + initialRefCount + "] expected a value > [0] - reader is already closed";
|
|
|
+ inFlightSearchers.add(this, wrappedSearcher.source());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void close() {
|
|
|
+ synchronized (lock) {
|
|
|
+ if (closed.compareAndSet(false, true)) {
|
|
|
+ inFlightSearchers.remove(this);
|
|
|
+ firstReleaseStack = new RuntimeException();
|
|
|
+ final int refCount = wrappedSearcher.reader().getRefCount();
|
|
|
+ /*
|
|
|
+ * this assert seems to be paranoid but given LUCENE-5362 we
|
|
|
+ * better add some assertions here to make sure we catch any
|
|
|
+ * potential problems.
|
|
|
+ */
|
|
|
+ assert refCount > 0 : "IndexReader#getRefCount() was [" + refCount + "] expected a value > [0] - reader is already "
|
|
|
+ + " closed. Initial refCount was: [" + initialRefCount + "]";
|
|
|
+ try {
|
|
|
+ wrappedSearcher.close();
|
|
|
+ } catch (RuntimeException ex) {
|
|
|
+ logger.debug("Failed to release searcher", ex);
|
|
|
+ throw ex;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ AssertionError error = new AssertionError("Released Searcher more than once, source [" + wrappedSearcher.source()
|
|
|
+ + "]");
|
|
|
+ error.initCause(firstReleaseStack);
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|