[Enhancement] Forward cluster planning settings to the Analytics Engine unified query path - #5611
Conversation
PR Reviewer Guide 🔍(Review updated until commit 6e2d9ba)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to ab19073 Explore these optional code suggestions:
Previous suggestionsSuggestions up to commit 1a401a3
Suggestions up to commit 506411d
Suggestions up to commit a59cdb7
|
a59cdb7 to
506411d
Compare
|
Persistent review updated to latest commit 506411d |
…ry path
The Analytics Engine (unified query) path silently ignored several cluster
settings, always planning against UnifiedQueryContext.Builder's hardcoded
seed value regardless of what the operator configured. The default
(non-AE) pipeline honored them correctly.
Root cause: RestUnifiedQueryAction.applyClusterOverrides() forwarded only a
hand-picked subset of cluster settings into the UnifiedQueryContext, while
the builder independently seeds a default settings map. Any planning
setting present in the seed map but absent from the forward list regressed
to its default -- a two-lists-drift defect.
Settings fixed (all verified to reach the plan context only with this
change):
plugins.query.size_limit pinned to 10000
plugins.ppl.pattern.method pinned to SIMPLE_PATTERN
plugins.ppl.pattern.mode pinned to LABEL
plugins.ppl.pattern.max.sample.count pinned to 10
plugins.ppl.pattern.buffer.limit pinned to 100000
plugins.ppl.pattern.show.numbered.token pinned to false
plugins.ppl.values.max.limit read back null, so the configured
cap on values() never applied
Each has a cluster-side default identical to the seeded one, so behavior is
unchanged unless an operator explicitly configured the setting -- at which
point the configured value now takes effect.
Replaces the hand-maintained forwardClusterSetting calls with a single
FORWARDED_CLUSTER_SETTINGS allow-list, and adds a drift guard
(everySeededPlanningSettingIsClassified) asserting every key the builder
seeds is either forwarded or explicitly documented as excluded, so this
defect cannot recur silently.
Deliberately not forwarded:
plugins.calcite.enabled -- the unified path is Calcite-based by
definition and must force it on.
plugins.ppl.subsearch.maxout / plugins.ppl.join.subsearch_maxout --
seeded to 0 (unlimited) on purpose to keep LogicalSystemLimit out of
plans built by external consumers of the unified query API. These do
diverge from the cluster defaults (10000 / 50000) even when
unconfigured; whether the in-cluster REST path should override that is
a separate behavioral decision, tracked in opensearch-project#5735.
Testing: the four new unit tests each fail without this change
(expected:<BRAIN> but was:<SIMPLE_PATTERN>, expected:<100> but was:<null>,
expected:<500> but was:<10000>) and pass with it.
Signed-off-by: Jialiang Liang <jiallian@amazon.com>
506411d to
1a401a3
Compare
|
Persistent review updated to latest commit 1a401a3 |
Verified against a live composite/parquet analytics-engine cluster:
forwarding plugins.ppl.values.max.limit makes the AE route strictly
worse, not better.
The cap is applied by attaching a `limit` argument to the values()
aggregate, which lowers to array_agg(DISTINCT x, limit). The DataFusion
backend has no binding for that two-argument form, so once the setting
actually reaches the parser the query fails outright:
UnsupportedOperationException: Unable to find binding for call
array_agg(DISTINCT $0, $1)
served to the client as HTTP 500 "Internal error". Today the same query
merely ignores the cap and returns all values. Turning a silent no-op
into a hard failure is a regression, so the key stays unforwarded until
the backend can bind the limited form -- the gap already tracked by
Capability.VALUES_LIMIT_NOT_HONORED.
The remaining forwarded settings were confirmed end to end on the same
cluster, baseline build vs fixed build:
plugins.query.size_limit=2 6 rows -> 2 rows
plugins.query.size_limit=4 6 rows -> 4 rows
plugins.ppl.pattern.mode=AGGREGATION
baseline: 6 rows, schema [age, name, patterns_field] (ignored)
fixed: 1 row, schema [patterns_field, pattern_count, sample_logs]
Signed-off-by: Jialiang Liang <jiallian@amazon.com>
Signed-off-by: Jialiang Liang <ryanleeang@gmail.com>
|
Persistent review updated to latest commit 4ca83de |
Addresses review feedback on the FORWARDED_CLUSTER_SETTINGS javadoc. The prose had grown to ~28 lines and, worse, was incomplete: it named only four exclusions and omitted CALCITE_SUPPORT_ALL_JOIN_TYPES entirely. Re-derived the full set by tracing every getSettingValue reachable from the unified context's Settings -- SysLimit.fromSettings, AstBuilder / AstExpressionBuilder / AstBuildGuard (the parsers the context builds), UnresolvedPlanHelper, and CalcitePlanContext. Fourteen keys are read on that path: nine forwarded, five deliberately not. CALCITE_ENGINE_ENABLED unified path is Calcite by definition PPL_SUBSEARCH_MAXOUT seeded unlimited on purpose (opensearch-project#5735) PPL_JOIN_SUBSEARCH_MAXOUT likewise (opensearch-project#5735) PPL_VALUES_MAX_LIMIT forwarding 500s the route (opensearch-project#5736) CALCITE_SUPPORT_ALL_JOIN_TYPES never seeded; guard inactive (opensearch-project#5734) The javadoc is now a compact bulleted list of those five with an issue reference each -- shorter than before and, unlike before, complete. Rather than leaving that claim as prose, DELIBERATELY_NOT_FORWARDED in the test now carries all five and documentedExclusionsAreNotForwarded asserts none of them reaches the plan context, using a sentinel value so "forwarded" is distinguishable from "seeded" and from "absent". Verified the guard bites: adding PPL_VALUES_MAX_LIMIT to the forward list fails with "plugins.ppl.values.max.limit must not be forwarded ... Actual: -12345". This replaces the narrower valuesMaxLimitIsNotForwarded test. Signed-off-by: Jialiang Liang <jiallian@amazon.com> Signed-off-by: Jialiang Liang <ryanleeang@gmail.com>
|
Persistent review updated to latest commit ab19073 |
…mments Per review, the production javadoc is cut to the rule plus a pointer: the per-key exclusion reasons live in one place only, the test's DELIBERATELY_NOT_FORWARDED, which is also what pins them. Down from ~28 lines originally to 8. Issue references are removed from code comments in both files; the reasons stand on their own, and tracking belongs in the PR and issues rather than in comments that go stale. Also dropped the hardcoded "five" from the javadoc so the count cannot rot as the list changes -- the enumerated list is the answer. Signed-off-by: Jialiang Liang <jiallian@amazon.com> Signed-off-by: Jialiang Liang <ryanleeang@gmail.com>
|
Persistent review updated to latest commit 6e2d9ba |
Description
Several cluster settings were silently ignored on the Analytics Engine (unified query) path — the AE route always planned against
UnifiedQueryContext.Builder's hardcoded seed value regardless of the configured cluster value. The default (non-AE) pipeline honored them correctly.Root cause.
RestUnifiedQueryAction.applyClusterOverrides()forwarded only a hand-picked subset of cluster settings into theUnifiedQueryContext, whileUnifiedQueryContext.Builderindependently seeds a default settings map. Any planning setting present in the seed map but absent from the forward list silently regressed to its default. This is a two-lists-drift defect: the two lists are maintained independently, so the drift is invisible until someone configures the setting and it does nothing.Settings fixed
plugins.query.size_limit10000plugins.ppl.pattern.methodSIMPLE_PATTERNpatternsdefault ignoredplugins.ppl.pattern.modeLABELplugins.ppl.pattern.max.sample.count10plugins.ppl.pattern.buffer.limit100000plugins.ppl.pattern.show.numbered.tokenfalseEvery one of these has a cluster-side default identical to the seeded value, so behavior is unchanged unless an operator explicitly configured the setting — at which point the configured value now takes effect. No query that works today changes behavior on defaults.
Fix
forwardClusterSettingcalls with a singleFORWARDED_CLUSTER_SETTINGSallow-list — one source of truth for which cluster settings the unified path honors. This matches the existing shape of the sibling handlerRestQuerySettingsAction, which declares its own settings allow/deny lists the same way.everySeededPlanningSettingIsClassified) asserting that every key the builder seeds is either forwarded or explicitly listed as deliberately excluded, with a reason. Adding a key to the builder's seed map without classifying it now fails a test instead of silently regressing. The guard reads the seeded keys through the existing publicSettings#getSettings()API — no reflection, no new production API.Verified end to end on a live Analytics Engine cluster
Single-node cluster with
composite-engine,parquet-data-format,analytics-engine,analytics-backend-datafusion,analytics-backend-lucene+ this plugin, querying parquet-backed composite indices. Same cluster, same data, baseline build vs this PR's build — every forwarded key this PR adds:query.size_limit=2query.size_limit=4pattern.mode=AGGREGATION[age, name, patterns_field][patterns_field, pattern_count, sample_logs]pattern.method=BRAIN<*> <*> <*> <*> <*> <*>.<*>.<*>.<*> <*> <*> <*>user <*> logged in from <*IP*> at port <*>pattern.show.numbered.token=true<*> <*> <*> …<token1> <token2> <token3> …pattern.max.sample.count=2/=5pattern_countfield absent, sincepattern.modewas ignored toolen(sample_logs)= 2 / 5 respectivelyplugins.ppl.pattern.buffer.limitis forwarded by the same mechanism but is not covered by a live-cluster probe: its minimum is50000, so distinguishing values requires more buffered patterns than a probe dataset produces. It is covered only by the unit test asserting the value reaches the plan context.Deliberately not forwarded
plugins.calcite.enabled— the unified path is Calcite-based by definition and must force it on regardless of the cluster value.plugins.ppl.values.max.limit— forwarding this one makes the AE route strictly worse. The cap is applied by attaching alimitargument tovalues(), which lowers toarray_agg(DISTINCT x, limit); the DataFusion backend has no binding for that two-argument form. Confirmed on the cluster above: with the key forwarded,stats values(f)fails withUnsupportedOperationException: Unable to find binding for call array_agg(DISTINCT $0, $1), surfaced as HTTP 500 — where today it merely ignores the cap. Turning a silent no-op into a hard failure is a regression, so the key stays out until the backend can bind the limited form. Tracked in [BUG] plugins.ppl.values.max.limit cannot be honored on the Analytics Engine route (no binding for array_agg(DISTINCT x, limit)) #5736; the gap is already recorded in-repo asCapability.VALUES_LIMIT_NOT_HONORED.plugins.ppl.subsearch.maxout/plugins.ppl.join.subsearch_maxout— the builder seeds these to0(unlimited) on purpose, to keepLogicalSystemLimitout of plans built by external consumers of the unified query API. Note these do diverge from the cluster defaults (10000/50000) even when unconfigured, so the AE path currently runs subsearches unbounded. Whether the in-cluster REST path should override that documented choice is a separate behavioral decision, tracked in [BUG] PPL subsearch maxout settings do not apply on the Analytics Engine path (diverges at defaults) #5735.One further gap found during this work is also out of scope here, tracked in #5734:
plugins.calcite.all_join_types.allowedis not seeded, soAstBuilder.validateJoinTypereadsnulland itsconfig != null && !configcheck skips validation entirely — the high-cost-join guardrail is inactive on the AE path. Restoring it is a user-visible tightening, so it belongs in its own PR.Check List
--signoff.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.