Use a copy of the SearchExecutionContext for each Percolator execution#142765
Use a copy of the SearchExecutionContext for each Percolator execution#142765davidkyle merged 17 commits intoelastic:mainfrom
Conversation
ae52cce to
27b4398
Compare
benwtrent
left a comment
There was a problem hiding this comment.
y'all might find this interesting.
| // PercolateQuery.QueryStore function from multiple threads. Here the | ||
| // solution is to create an AutoPrefilteringScope for each invocation | ||
| // of PercolateQuery.QueryStore | ||
| var safeContext = new FilteredSearchExecutionContext(context) { |
There was a problem hiding this comment.
I am not sure that Percolator should be reading/writing from the auto-prefiltering scope at all.
I really dislike that this was just added for all boolean queries, when its only needed in very specific cases for knn searches.
There was a problem hiding this comment.
I think this autoPrefilteringScope should be null and not available at all unless the very specific query asks for it. Otherwise we end up with weird latent bugs for code paths that aren't actually necessary or expected.
There was a problem hiding this comment.
@benwtrent autoPrefilteringScope uses the same design pattern as nestedScope, which is one of the reasons we went with it. This leads to an immediate follow-up question: Are there similar potential issues with nested percolated queries?
There was a problem hiding this comment.
Indeed, this issue seems to happen with nestedScope as well: #141489
I've been trying to reproduce this with percolated nested queries locally, but haven't had much luck yet. Such is the nature of concurrency bugs :/
There was a problem hiding this comment.
I added a copyForConcurrentUse() method to SearchExecutionContext that addresses both the AutoPrefilteringScope and NestedScope issues.
There was a problem hiding this comment.
I really dislike that this was just added for all boolean queries, when its only needed in very specific cases for knn searches.
I explored walking the query tree to find a knn query and only enabling auto pre-filtering if present and ended up with logic not dissimilar to this https://github.com/elastic/elasticsearch/blob/main/server/src/main/java/org/elasticsearch/index/query/support/AutoPrefilteringUtils.java#L45
There doesn't seem to be a blessed way to walk the query tree as getting the child queries for each query type has to be handled differently. It feels out of scope for this bug fix, we can explore the best way to handle this in another PR possibly by adding a List<QueryBuilder> subQueries() function to QueryBuilder
There was a problem hiding this comment.
++ for exploring other ways to walk the query tree in a separate PR. I think this one should focus only on making SearchExecutionContext usable by percolator.
modules/percolator/src/test/java/org/elasticsearch/percolator/QueryBuilderStoreTests.java
Show resolved
Hide resolved
| // The context's NestedScope is also vulnerable to concurrent modification. | ||
| // Use a cloned SearchExecutionContext for each thread with new instances of | ||
| // the mutable fields. | ||
| var safeContext = context.copyForConcurrentUse(); |
There was a problem hiding this comment.
The context here is actually a FilteredSearchExecutionContext create at https://github.com/elastic/elasticsearch/blob/main/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorFieldMapper.java#L581
Mikep86
left a comment
There was a problem hiding this comment.
Partial review, I can finish up tomorrow
server/src/main/java/org/elasticsearch/index/query/FilteredSearchExecutionContext.java
Outdated
Show resolved
Hide resolved
Mikep86
left a comment
There was a problem hiding this comment.
I took a closer look at this and I think the current solution is both too complicated and fundamentally flawed.
We create the FilteredSearchExecutionContext for the percolator query in wrapAllEmptyTextFields. However, when copying it in createStore, we generate a new FilteredSearchExecutionContext instance that does not persist the anonymously overridden methods. The whole reason for using FilteredSearchExecutionContext is lost by generating a copy.
There is a simpler solution that both generates a copy of SearchExecutionContext and persists the anonymously overridden methods: Configure the context late in createStore. The steps would be:
- Refactor
configureContextand downstream methods as necessary so they are callable fromcreateStore. - Simplify the implementation of
wrapAllEmptyTextFieldsto:
static SearchExecutionContext wrapAllEmptyTextFields(SearchExecutionContext searchExecutionContext) {
return new SearchExecutionContext(searchExecutionContext) {
@Override
public boolean fieldExistsInIndex(String fieldname) {
return true;
}
};
}
This both generates a copy of the context (fixing this bug) and anonymously overrides the desired methods in each copy.
- Call
configureContextincreateStore - We can completely remove
FilteredSearchExecutionContextat this point. It is only used by the percolator query, which no longer needs it.
@benwtrent WDYT?
|
Thanks for the simplification @Mikep86 I ended up doing some Yak shaving on this one. The combination of Then I've pretty much done as suggested to move the creation of the new context to |
benwtrent
left a comment
There was a problem hiding this comment.
This ended up in a nice place. Thank you @Mikep86 @davidkyle !!!
| } | ||
|
|
||
| executionContext = configureContext(executionContext, isMapUnmappedFieldAsText()); | ||
| executionContext = PercolateQueryBuilder.newPercolateSearchContext(executionContext, isMapUnmappedFieldAsText()); |
There was a problem hiding this comment.
It's great that we're using a SearchExecutionContext configured similarly at both document parsing time and at query time. There were probably other bugs we hadn't found yet caused by using differently configured contexts here vs. at query time...
💚 All backports created successfully
Questions ?Please refer to the Backport tool documentation |
elastic#142765) (cherry picked from commit 22b4577)
…locations * upstream/main: (126 commits) Update KnnIndexTester to use more settings from datasets (elastic#143869) fix: dynamic template vector array is overridden by automatic dense_vector mapping (elastic#143733) ES|QL: Don't reuse the same alias for _fork column (elastic#143909) Close and initialize clients after each node upgrade in logsdb rolling upgrade tests. (elastic#143823) ESQL: Added GroupedTopNOperator for LIMIT BY, compute only (elastic#143476) Handle views in ResolveIndexAction (elastic#143561) Improve reindex rethrottle API in stateless (elastic#143771) Use a copy of the SearchExecutionContext for each Percolator execution (elastic#142765) Log the stacktrace when we encounter a deprecation warning for `default_metric` (elastic#143929) ESQL: evaluate ReferenceAttributes to potentially FieldAttributes for full-text functions restriction (elastic#143893) Add ClusterStateSerializationStats Serializatation Tests (elastic#142703) Adds Coordination Diagnostics Tests (elastic#142709) Upgrade Elasticsearch to Apache Lucene 10.4 (elastic#141882) ESQL: Add configurable bracket-based multi-value support for CSV reader (elastic#143890) time series es819 binary dv use up to a 1mb block size (elastic#143049) Dynamically enable / disable plugins in correspondence to stateless mode. (elastic#142147) ES|QL: Implement first/last_over_time for tdigest (elastic#143832) Document CHANGE_POINT limitation (elastic#143877) Fix OperationsOnSeqNoDisabledIndicesIT (elastic#143892) [Test] Test that sequence numbers are not pruned with retention lease (elastic#143825) ...
|
@davidkyle do we know if this bug also exists in 8.19? I wonder if we want to backport there as well. |
💚 All backports created successfully
Questions ?Please refer to the Backport tool documentation |
elastic#142765) (cherry picked from commit 22b4577) # Conflicts: # modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java # modules/percolator/src/test/java/org/elasticsearch/percolator/QueryBuilderStoreTests.java
|
@davidkyle Sorry to pile on, but if we're backporting to 8.19, we should backport to 9.2 as well. |
💚 All backports created successfully
Questions ?Please refer to the Backport tool documentation |
elastic#142765) (cherry picked from commit 22b4577) # Conflicts: # modules/percolator/src/test/java/org/elasticsearch/percolator/QueryBuilderStoreTests.java
|
Thanks for the shout @Mikep86. When I saw there are no more branches I wept for there were no more backports to make |
…ecution (#142765) (#144027) * Use a copy of the SearchExecutionContext for each Percolator execution (#142765) (cherry picked from commit 22b4577) # Conflicts: # modules/percolator/src/test/java/org/elasticsearch/percolator/QueryBuilderStoreTests.java * [CI] Auto commit changes from spotless * fix comp --------- Co-authored-by: elasticsearchmachine <infra-root+elasticsearchmachine@elastic.co>
The
AutoPrefilteringScopemember ofSearchExecutionContextis not thread safe but Percolate accesses the member from multiple threads resulting in the stack trace below.SearchExecutionContext::NestedScopecan also be used in non-thread safe manner in Percolator query.This PR adds a
copyForConcurrentUse()toSearchExecutionContextwhich creates a shallow copy of the context with new instances of the mutable members. These members are no longer shared between the threads removing the concurrent access issues.Closes #141489