Improve EngineSearcher tracking

Currently we fail tests is any searcher reference is pending. Yet,
on a slow machine the freeContext calls that are async could still be
in flight so if there are pending searchers we wait for a bit to make
sure we don't fail if a freeContext call is in flight.
The MockEngine now also contains the stack trace of the first close call
if a searcher is closed twice.
This commit is contained in:
Simon Willnauer 2013-09-25 14:16:11 +02:00
parent 57d6944f0f
commit 500469fd28
2 changed files with 29 additions and 5 deletions

View File

@ -49,11 +49,11 @@ public abstract class ElasticSearchTestCase extends AbstractRandomizedTest {
public static final String CHILD_VM_ID = System.getProperty("junit4.childvm.id", "" + System.currentTimeMillis()); public static final String CHILD_VM_ID = System.getProperty("junit4.childvm.id", "" + System.currentTimeMillis());
public boolean awaitBusy(Predicate<?> breakPredicate) throws InterruptedException { public static boolean awaitBusy(Predicate<?> breakPredicate) throws InterruptedException {
return awaitBusy(breakPredicate, 10, TimeUnit.SECONDS); return awaitBusy(breakPredicate, 10, TimeUnit.SECONDS);
} }
public boolean awaitBusy(Predicate<?> breakPredicate, long maxWaitTime, TimeUnit unit) throws InterruptedException { public static boolean awaitBusy(Predicate<?> breakPredicate, long maxWaitTime, TimeUnit unit) throws InterruptedException {
long maxTimeInMillis = TimeUnit.MILLISECONDS.convert(maxWaitTime, unit); long maxTimeInMillis = TimeUnit.MILLISECONDS.convert(maxWaitTime, unit);
long iterations = Math.max(Math.round(Math.log10(maxTimeInMillis) / Math.log10(2)), 1); long iterations = Math.max(Math.round(Math.log10(maxTimeInMillis) / Math.log10(2)), 1);
long timeInMillis = 1; long timeInMillis = 1;
@ -102,9 +102,22 @@ public abstract class ElasticSearchTestCase extends AbstractRandomizedTest {
} }
public static void ensureAllSearchersClosed() { public static void ensureAllSearchersClosed() {
/* in some cases we finish a test faster than the freeContext calls make it to the
* shards. Let's wait for some time if there are still searchers. If the are really
* pending we will fail anyway.*/
try {
if (awaitBusy(new Predicate<Object>() {
public boolean apply(Object o) {
return MockRobinEngine.INFLIGHT_ENGINE_SEARCHERS.isEmpty();
}
}, 5, TimeUnit.SECONDS)) {
return;
}
} catch (InterruptedException ex) {
if (MockRobinEngine.INFLIGHT_ENGINE_SEARCHERS.isEmpty()) { if (MockRobinEngine.INFLIGHT_ENGINE_SEARCHERS.isEmpty()) {
return; return;
} }
}
try { try {
RuntimeException ex = null; RuntimeException ex = null;
StringBuilder builder = new StringBuilder("Unclosed Searchers instance for shards: ["); StringBuilder builder = new StringBuilder("Unclosed Searchers instance for shards: [");

View File

@ -95,6 +95,8 @@ public final class MockRobinEngine extends RobinEngine implements Engine {
public static final class AssertingSearcher implements Searcher { public static final class AssertingSearcher implements Searcher {
private final Searcher searcher; private final Searcher searcher;
private final ShardId shardId; private final ShardId shardId;
private RuntimeException firstReleaseStack;
private final Object lock = new Object();
public AssertingSearcher(Searcher searcher, ShardId shardId) { public AssertingSearcher(Searcher searcher, ShardId shardId) {
this.searcher = searcher; this.searcher = searcher;
@ -110,7 +112,16 @@ public final class MockRobinEngine extends RobinEngine implements Engine {
@Override @Override
public boolean release() throws ElasticSearchException { public boolean release() throws ElasticSearchException {
RuntimeException remove = INFLIGHT_ENGINE_SEARCHERS.remove(this); RuntimeException remove = INFLIGHT_ENGINE_SEARCHERS.remove(this);
assert remove != null : "Released Searcher more than once, source [" + searcher.source() + "]"; synchronized (lock) {
// make sure we only get this once and store the stack of the first caller!
if (remove == null) {
assert firstReleaseStack != null;
throw new AssertionError("Released Searcher more than once, source [" + searcher.source() + "]", firstReleaseStack);
} else {
assert firstReleaseStack == null;
firstReleaseStack = new RuntimeException("Searcher Released first here, source [" + searcher.source() + "]");
}
}
return searcher.release(); return searcher.release();
} }