feat: complex query thread pool#5628
Conversation
Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
PR Reviewer Guide 🔍(Review updated until commit 1053561)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to 1053561 Explore these optional code suggestions:
Previous suggestionsSuggestions up to commit aaaa8e4
Suggestions up to commit aaaa8e4
Suggestions up to commit 009300a
Suggestions up to commit 9ba8eef
Suggestions up to commit 21dd714
|
Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
|
Persistent review updated to latest commit 46c3cbe |
|
Persistent review updated to latest commit 6fdd82e |
Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
|
Persistent review updated to latest commit 434021f |
|
Persistent review updated to latest commit 169375f |
Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
|
Persistent review updated to latest commit 9c57cd7 |
Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
e04d775 to
559a2d0
Compare
|
Persistent review updated to latest commit 559a2d0 |
1 similar comment
|
Persistent review updated to latest commit 559a2d0 |
| () -> { | ||
| try (PreparedStatement statement = OpenSearchRelRunners.run(context, rel)) { | ||
| try (PreparedStatement statement = | ||
| OpenSearchRelRunners.run(context, CalciteToolsHelper.optimize(rel, context))) { |
There was a problem hiding this comment.
Optimize already been called in QueryService. Why call optimize again?
There was a problem hiding this comment.
ah yeah, my bad -- I was moving optimize out of 'execute' since we need the optimized plan to make complex branch decisions, and did a ctrl+f to find old execute/run call sites and make sure they called optimize.
| if (currentTime >= 0) { | ||
| hookHandle = | ||
| Hook.CURRENT_TIME.addThread( | ||
| (Consumer<org.apache.calcite.util.Holder<Long>>) h -> h.set(currentTime)); | ||
| } | ||
| CalcitePlanContext.stripNullColumns.set(stripNullCols); | ||
| CalcitePlanContext.timewrapUnitName.set(twUnitName); | ||
| CalcitePlanContext.timewrapSeries.set(twSeries); |
There was a problem hiding this comment.
why L87-L94 change required? seems not releated to threadpool change.
There was a problem hiding this comment.
They're thread-local state associated with the calcite context that needs to be copied or they get dropped. In particular the current_time hook bits are related to integ test failures with time queries involving NOW(), and the others are just copied for following the same pattern.
To make the intent of this clearer I moved all of this copy logic into snapshot methods in CalcitePlanContext. Should also make it so if the thread context is modified in the future, this branch is less likely to be missed.
| CalcitePlanContext context, | ||
| Runnable task, | ||
| @Nullable ResponseListener<?> failureListener) { | ||
| if (isComplexPoolEnabled() && ScriptDetector.hasScripts(optimizedPlan)) { |
There was a problem hiding this comment.
When query schedule in complex-worker thread, the complex query timeout control OpenSearchQueryManager does not take effect.
There was a problem hiding this comment.
It's applying but slowly, I poked more and it's because script-based complex queries can spend very long delays in DSL (compared to e.g. joins where individual DSL pages are fast). I added some polling so the PPL thread interrupts faster in these cases. Notably we still leave zombie search tasks behind, but this is a preexisting issue and they're still usually much faster than the overall query (involving multiple pages -- in testing I got no sub-searches beyond 10 seconds or so).
I can follow up doing more aggressive cancelation that propagates to the search task if we need it but I believe the polling impl is enough for this PR.
| StageErrorHandler.executeStageVoid( | ||
| QueryProcessingStage.EXECUTING, | ||
| () -> executionEngine.execute(calcitePlan, context, listener), | ||
| () -> executionDispatcher.dispatch(optimizedPlan, context, listener, executionEngine), |
There was a problem hiding this comment.
Calcite fallback exception will not catched if it is complex query. The reasons is dispatch() returns success (the schedule call didn't throw), so executeStageVoid records the EXECUTING stage as successful. The real error surfaces later, on the complex thread, and is delivered raw via listener.onFailure(e). It never passes through executeStageVoid's wrapping (no EXECUTING stage/location on the ErrorReport), never reaches the overflow-translation catch, and never triggers V2 fallback.
There was a problem hiding this comment.
yeah I didn't think about that... It's hard to add handling here because the fallback calls to another path back in the simple pool, we'd I think need some sort of callback that sends the request back over, or reimplement that side of the logic in the complex pool.
I think it's acceptable to not handle this case because we currently have no logic to fallback after execution, those decisions are all made at planning time before the thread handoff. And I don't expect we'll add any new execution-time fallback logic in the future, seems like these sites should be trending toward 0 over time.
| Object planSnapshot = | ||
| enginePlan != null ? enginePlan : (planRoot == null ? null : planRoot.snapshot()); | ||
| profile = new QueryProfile(totalMillis, snapshot, planSnapshot); | ||
| String threadPool = CalcitePlanContext.executionPool.get(); |
There was a problem hiding this comment.
finish() is invoked from the response formatter. QueryProfile.thread_pool is read on the wrong thread → always null/wrong for complex-pool queries
There was a problem hiding this comment.
This execution pool context is set with the thread state copy over near https://github.com/opensearch-project/sql/pull/5628/changes#r3616809250, empirically this works. I added an IT for complex profiling correctly marking its thread pool.
Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
|
Persistent review updated to latest commit 73c68cf |
Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
|
Persistent review updated to latest commit 21dd714 |
Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
|
Persistent review updated to latest commit 9ba8eef |
Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
|
Persistent review updated to latest commit 009300a |
Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
Signed-off-by: Simeon Widdis <sawiddis@amazon.com>
7580ddb to
aaaa8e4
Compare
|
Persistent review updated to latest commit 7580ddb |
|
Persistent review updated to latest commit aaaa8e4 |
|
Persistent review updated to latest commit 1053561 |
Description
Following discussion on #5608, this PR implements a dedicated thread pool for slow queries. This helps keep the cluster responsive in cases of specific expensive queries being run, as the main worker thread pool will still be available to process fast queries. This comes with a
plugins.sql.slow_worker_pool.enabledsetting (default true).The pool exists as a separate resource that the worker can hand off to. So the normal fast query path is unchanged, we only hand off in known special cases. This also means we only pay rescheduling overhead for queries that are already slow.
I've tested this by saturating a cluster with >20sec queries (until all slow threads are active and requests are queued), and then verified that normal cheap queries are still responsive even if slow queries are at capacity.
Guide for reviewers:
ScriptDetector.java. We primary look for specific expensive UDFs (rex, parse), window functions, joins, or any script pushdown contexts.ThreadPoolExecutionDispatcher, if we hit theScriptDetectorcheck we do a handoff involving a lot of thread context propagation and cleanup. This is the riskiest part of the code, I've been carefully checking that the context propagation is working correctly and that we handle things like auth context (field level security) and cancellation.Related Issues
Resolves #5608
Check List
--signoffor-s.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.