-
Notifications
You must be signed in to change notification settings - Fork 25.6k
Fix missing removal of query cancellation callback in QueryPhase #130279
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
f5a5daa
cf1d49f
6e4edce
5fc5f49
103a1c1
7314f4d
57da03a
15c3582
0014df9
9bc2874
ae84b60
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
pr: 130279 | ||
summary: Fix missing removal of query cancellation callback in QueryPhase | ||
area: Search | ||
type: bug | ||
issues: [130071] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -456,6 +456,24 @@ public ShardSearchRequest request() { | |
return context; | ||
} | ||
|
||
public void testQueryCancellationCallbackRemovedWhenPartialAllowed() throws Exception { | ||
TrackingSearcher searcher = new TrackingSearcher(reader); | ||
try (TestSearchContext ctx = createSearchContextWithAllowPartialResults(searcher, true)) { | ||
QueryPhase.executeQuery(ctx); | ||
assertNotNull("callback should be registered", searcher.added); | ||
} | ||
assertTrue("callback should be removed", searcher.removed); | ||
} | ||
|
||
public void testQueryCancellationCallbackNotRemovedWhenPartialDisallowed() throws Exception { | ||
TrackingSearcher searcher = new TrackingSearcher(reader); | ||
try (TestSearchContext ctx = createSearchContextWithAllowPartialResults(searcher, false)) { | ||
QueryPhase.executeQuery(ctx); | ||
assertNotNull("callback should be registered", searcher.added); | ||
} | ||
assertFalse("callback must stay registered for later phases", searcher.removed); | ||
|
||
} | ||
|
||
private static final class TestSuggester extends Suggester<TestSuggestionContext> { | ||
private final ContextIndexSearcher contextIndexSearcher; | ||
|
||
|
@@ -572,4 +590,45 @@ public final boolean isCacheable(LeafReaderContext ctx) { | |
return false; | ||
} | ||
} | ||
|
||
private TestSearchContext createSearchContextWithAllowPartialResults(TrackingSearcher searcher, boolean allowPartial) { | ||
TestSearchContext context = new TestSearchContext(null, indexShard, searcher) { | ||
|
||
@Override | ||
public ShardSearchRequest request() { | ||
SearchRequest req = new SearchRequest(); | ||
req.allowPartialSearchResults(allowPartial); | ||
return new ShardSearchRequest(OriginalIndices.NONE, req, indexShard.shardId(), 0, 1, AliasFilter.EMPTY, 1F, 0, null); | ||
} | ||
}; | ||
|
||
context.parsedQuery(new ParsedQuery(new MatchAllDocsQuery())); | ||
|
||
return context; | ||
} | ||
|
||
private class TrackingSearcher extends ContextIndexSearcher { | ||
Runnable added; | ||
boolean removed; | ||
|
||
TrackingSearcher(IndexReader reader) throws IOException { | ||
super( | ||
reader, | ||
IndexSearcher.getDefaultSimilarity(), | ||
IndexSearcher.getDefaultQueryCache(), | ||
LuceneTestCase.MAYBE_CACHE_POLICY, | ||
true | ||
); | ||
} | ||
|
||
@Override | ||
public void addQueryCancellation(Runnable r) { | ||
added = r; | ||
} | ||
|
||
@Override | ||
public void removeQueryCancellation(Runnable r) { | ||
if (r == added) removed = true; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that this is not wrong, but perhaps incomplete: we duplicate the low level cancellation runnable registered in
DefaultSearchContext#preProcess
as well, so I think that we should rather clear all the cancellation checks.One way to do that would be to recreate the searcher before executing the fetch phase for the single shard case, but I see that requires quite some changes, as we don't want to rebuild the entire search context which is a rather heavy object.
Another way would be to call the existing
ContextIndexSearcher#close
method, but reusing the searcher after closing it sounds like an anti-pattern, although it would work in this case (relying on the fact that close only clears the cancellation runnables).Maybe a better way would be to replace the current
removeQueryCancellation
method with aremoveQueryCancellations
that clears them all like close does. I would still call it tough only where needed, meaning in the only place where we effectively reuse the context/searcher. Otherwise, it is not evident why we need to do so in query phase as opposed to other places.I prefer this explicit treatment before executing fetch for the single shard scenario, because it addresses the edge case, and I am not sure we want to handle it in a generic manner by removing cancellation checks where searchers are normally not reused across phases.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By the way, I don't think we need the conditional based on
allowPartialSearchResults
. If we do not allow partial search results, a hard error will be thrown at the end of the query phase before doing fetch if there's a timeout.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated the code with those changes.
Absolutely, I completely agree!
I believe the latest changes address the feedback, but let me know if you'd like anything refined further.