Skip to content

Commit 5066835

Browse files
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
1 parent c71d959 commit 5066835

File tree

2 files changed

+34
-3
lines changed

2 files changed

+34
-3
lines changed

server/src/main/java/org/elasticsearch/search/SearchService.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -626,11 +626,12 @@ private DefaultSearchContext createSearchContext(ShardSearchRequest request, Tim
626626
indexShard.shardId(), request.getClusterAlias(), OriginalIndices.NONE);
627627
Engine.Searcher searcher = indexShard.acquireSearcher(source);
628628

629-
final DefaultSearchContext searchContext = new DefaultSearchContext(idGenerator.incrementAndGet(), request, shardTarget,
630-
searcher, clusterService, indexService, indexShard, bigArrays, threadPool::relativeTimeInMillis, timeout,
631-
fetchPhase, clusterService.state().nodes().getMinNodeVersion());
632629
boolean success = false;
630+
DefaultSearchContext searchContext = null;
633631
try {
632+
searchContext = new DefaultSearchContext(idGenerator.incrementAndGet(), request, shardTarget,
633+
searcher, clusterService, indexService, indexShard, bigArrays, threadPool::relativeTimeInMillis, timeout,
634+
fetchPhase, clusterService.state().nodes().getMinNodeVersion());
634635
// we clone the query shard context here just for rewriting otherwise we
635636
// might end up with incorrect state since we are using now() or script services
636637
// during rewrite and normalized / evaluate templates etc.
@@ -641,6 +642,11 @@ private DefaultSearchContext createSearchContext(ShardSearchRequest request, Tim
641642
} finally {
642643
if (success == false) {
643644
IOUtils.closeWhileHandlingException(searchContext);
645+
if (searchContext == null) {
646+
// we handle the case where the DefaultSearchContext constructor throws an exception since we would otherwise
647+
// leak a searcher and this can have severe implications (unable to obtain shard lock exceptions).
648+
IOUtils.closeWhileHandlingException(searcher);
649+
}
644650
}
645651
}
646652
return searchContext;

server/src/test/java/org/elasticsearch/search/SearchServiceTests.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.elasticsearch.action.search.SearchRequest;
3131
import org.elasticsearch.action.search.SearchResponse;
3232
import org.elasticsearch.action.search.SearchTask;
33+
import org.elasticsearch.action.search.SearchType;
3334
import org.elasticsearch.action.support.IndicesOptions;
3435
import org.elasticsearch.action.support.PlainActionFuture;
3536
import org.elasticsearch.action.support.WriteRequest;
@@ -680,4 +681,28 @@ public void testCreateSearchContext() throws IOException {
680681
assertSame(searchShardTarget, searchContext.fetchResult().getSearchShardTarget());
681682
}
682683
}
684+
685+
/**
686+
* While we have no NPE in DefaultContext constructor anymore, we still want to guard against it (or other failures) in the future to
687+
* avoid leaking searchers.
688+
*/
689+
public void testCreateSearchContextFailure() throws IOException {
690+
final String index = randomAlphaOfLengthBetween(5, 10).toLowerCase(Locale.ROOT);
691+
final IndexService indexService = createIndex(index);
692+
final SearchService service = getInstanceFromNode(SearchService.class);
693+
final ShardId shardId = new ShardId(indexService.index(), 0);
694+
695+
NullPointerException e = expectThrows(NullPointerException.class,
696+
() -> service.createContext(
697+
new ShardSearchLocalRequest(shardId, null, 0, null) {
698+
@Override
699+
public SearchType searchType() {
700+
// induce an artificial NPE
701+
throw new NullPointerException("expected");
702+
}
703+
}
704+
));
705+
assertEquals("expected", e.getMessage());
706+
assertEquals("should have 2 store refs (IndexService + InternalEngine)", 2, indexService.getShard(0).store().refCount());
707+
}
683708
}

0 commit comments

Comments
 (0)