Fix SearchService.createContext exception handling (#46258)
An exception from the DefaultSearchContext constructor could leak a searcher, causing future issues like shard lock obtained exceptions. The underlying cause of the exception in the constructor has been fixed, but as a safety precaution we also fix the exception handling in createContext. Closes #45378
This commit is contained in:
parent
c71d959d61
commit
5066835569
|
@ -626,11 +626,12 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv
|
||||||
indexShard.shardId(), request.getClusterAlias(), OriginalIndices.NONE);
|
indexShard.shardId(), request.getClusterAlias(), OriginalIndices.NONE);
|
||||||
Engine.Searcher searcher = indexShard.acquireSearcher(source);
|
Engine.Searcher searcher = indexShard.acquireSearcher(source);
|
||||||
|
|
||||||
final DefaultSearchContext searchContext = new DefaultSearchContext(idGenerator.incrementAndGet(), request, shardTarget,
|
boolean success = false;
|
||||||
|
DefaultSearchContext searchContext = null;
|
||||||
|
try {
|
||||||
|
searchContext = new DefaultSearchContext(idGenerator.incrementAndGet(), request, shardTarget,
|
||||||
searcher, clusterService, indexService, indexShard, bigArrays, threadPool::relativeTimeInMillis, timeout,
|
searcher, clusterService, indexService, indexShard, bigArrays, threadPool::relativeTimeInMillis, timeout,
|
||||||
fetchPhase, clusterService.state().nodes().getMinNodeVersion());
|
fetchPhase, clusterService.state().nodes().getMinNodeVersion());
|
||||||
boolean success = false;
|
|
||||||
try {
|
|
||||||
// we clone the query shard context here just for rewriting otherwise we
|
// we clone the query shard context here just for rewriting otherwise we
|
||||||
// might end up with incorrect state since we are using now() or script services
|
// might end up with incorrect state since we are using now() or script services
|
||||||
// during rewrite and normalized / evaluate templates etc.
|
// during rewrite and normalized / evaluate templates etc.
|
||||||
|
@ -641,6 +642,11 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv
|
||||||
} finally {
|
} finally {
|
||||||
if (success == false) {
|
if (success == false) {
|
||||||
IOUtils.closeWhileHandlingException(searchContext);
|
IOUtils.closeWhileHandlingException(searchContext);
|
||||||
|
if (searchContext == null) {
|
||||||
|
// we handle the case where the DefaultSearchContext constructor throws an exception since we would otherwise
|
||||||
|
// leak a searcher and this can have severe implications (unable to obtain shard lock exceptions).
|
||||||
|
IOUtils.closeWhileHandlingException(searcher);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return searchContext;
|
return searchContext;
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.elasticsearch.action.search.SearchPhaseExecutionException;
|
||||||
import org.elasticsearch.action.search.SearchRequest;
|
import org.elasticsearch.action.search.SearchRequest;
|
||||||
import org.elasticsearch.action.search.SearchResponse;
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
import org.elasticsearch.action.search.SearchTask;
|
import org.elasticsearch.action.search.SearchTask;
|
||||||
|
import org.elasticsearch.action.search.SearchType;
|
||||||
import org.elasticsearch.action.support.IndicesOptions;
|
import org.elasticsearch.action.support.IndicesOptions;
|
||||||
import org.elasticsearch.action.support.PlainActionFuture;
|
import org.elasticsearch.action.support.PlainActionFuture;
|
||||||
import org.elasticsearch.action.support.WriteRequest;
|
import org.elasticsearch.action.support.WriteRequest;
|
||||||
|
@ -680,4 +681,28 @@ public class SearchServiceTests extends ESSingleNodeTestCase {
|
||||||
assertSame(searchShardTarget, searchContext.fetchResult().getSearchShardTarget());
|
assertSame(searchShardTarget, searchContext.fetchResult().getSearchShardTarget());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* While we have no NPE in DefaultContext constructor anymore, we still want to guard against it (or other failures) in the future to
|
||||||
|
* avoid leaking searchers.
|
||||||
|
*/
|
||||||
|
public void testCreateSearchContextFailure() throws IOException {
|
||||||
|
final String index = randomAlphaOfLengthBetween(5, 10).toLowerCase(Locale.ROOT);
|
||||||
|
final IndexService indexService = createIndex(index);
|
||||||
|
final SearchService service = getInstanceFromNode(SearchService.class);
|
||||||
|
final ShardId shardId = new ShardId(indexService.index(), 0);
|
||||||
|
|
||||||
|
NullPointerException e = expectThrows(NullPointerException.class,
|
||||||
|
() -> service.createContext(
|
||||||
|
new ShardSearchLocalRequest(shardId, null, 0, null) {
|
||||||
|
@Override
|
||||||
|
public SearchType searchType() {
|
||||||
|
// induce an artificial NPE
|
||||||
|
throw new NullPointerException("expected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
));
|
||||||
|
assertEquals("expected", e.getMessage());
|
||||||
|
assertEquals("should have 2 store refs (IndexService + InternalEngine)", 2, indexService.getShard(0).store().refCount());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue