Lower frontend-private rel nodes before the analytics-engine handoff (fix dedup 500) - #5695
Conversation
PR Reviewer Guide 🔍(Review updated until commit bf03399)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to bf03399 Explore these optional code suggestions:
Previous suggestionsSuggestions up to commit 55c51fd
Suggestions up to commit fad6e50
|
| // optimize() collapses dedup back into a frontend-private LogicalDedup (for the | ||
| // Lucene pushdown path); the analytics engine can't mark that node, so lower it | ||
| // (and any other frontend-private rel) into a standard Calcite shape here. | ||
| plan = AnalyticsPlanLowering.lower(plan); |
There was a problem hiding this comment.
LogicalDedup is added becuase Pushdown needed it? If true, The other options is in RelNodeVisitor avoid generated LogicalDedup when use analytics engine.
There was a problem hiding this comment.
Good call — done. Rather than generate a LogicalDedup and convert it back, the analytics route now just doesn't generate it: PPLSimplifyDedupRule only exists to collapse the ROW_NUMBER composite into a LogicalDedup for the Lucene DedupPushdownRule, and the analytics engine has no such pushdown. Added CalciteToolsHelper.optimizeForAnalytics (the same rules minus that one) and call it on the analytics route, so the engine gets the standard ROW_NUMBER window form directly.
fad6e50 to
55c51fd
Compare
|
Persistent review updated to latest commit 55c51fd |
| plan, planContext); | ||
| // Lower PPL rel nodes (e.g. LogicalDedup) that the analytics engine cannot | ||
| // mark. | ||
| plan = AnalyticsPlanLowering.lower(plan); |
There was a problem hiding this comment.
- At line 237, we've triggered optimizations already?
- I think both lines changed the previous boundary (SQL/PPL generates raw plan and backend like AE responsible for optimizations). Shall we move both into AnalyticsExecutionEngine and later if needed explicitly call new UnifiedQueryPlanner.optmize or UnifiedQueryOptimizer API?
There was a problem hiding this comment.
I'd like to re-think our SQL → AE integration. We've accumulated a lot of one-offs in AE, substrait adapters that exist to accommodate PPL specific operators and a test time circular dependency since AE's integration tests now need the SQL plugin installed to produce plans and test e2e.
Thinking AE should publish a contract and have front-ends like SQL/PPL compile and optimize to it. AE validates against it at the boundary and rejects up front instead of accepting whatever arrives and failing late in substrait conversion or out on a data node.
There was a problem hiding this comment.
Reworked since your comment — dropped lowering; the route now calls optimizeForAnalytics (rules minus PPLSimplifyDedupRule), so no LogicalDedup is generated.
Line 237 already optimized (predates this PR, #5656 — just swapped it). And it can't move into AnalyticsExecutionEngine yet — dispatchTask() routes thread pools via ScriptDetector on the optimized plan before execute().
Agree the boundary cleanup + Marc's AE-contract idea are worth a follow-up.
PPL `dedup` followed by any pipe stage failed with a 500 on the analytics route: `IllegalStateException: Project rule encountered unmarked child [LogicalDedup]`. `dedup` is planned as a ROW_NUMBER() OVER (PARTITION BY keys) + Filter composite. PPLSimplifyDedupRule (run during CalciteToolsHelper.optimize) collapses that composite into a LogicalDedup so DedupPushdownRule can push it into a Lucene scan. The analytics engine has no such pushdown and cannot plan a LogicalDedup, so the node survives to its marking phase and fails. Add CalciteToolsHelper.optimizeForAnalytics, which runs the same rules minus PPLSimplifyDedupRule, and call it from the analytics route in RestUnifiedQueryAction. The analytics engine then receives the standard ROW_NUMBER window form directly. The Lucene/Calcite path is unchanged. Resolves #22671. Signed-off-by: Kai Huang <ahkcs@amazon.com>
55c51fd to
bf03399
Compare
|
Persistent review updated to latest commit bf03399 |
Description
PPL
dedupfollowed by any pipe stage fails with a 500 on the analytics-engine route:Server-side:
IllegalStateException: Project rule encountered unmarked child [LogicalDedup]atOpenSearchProjectRule.onMatch. Deterministic for everydedup … | <anything>shape.Resolves #22671.
Root cause
dedupis planned as aROW_NUMBER() OVER (PARTITION BY keys)+ Filter composite.CalciteToolsHelper.optimize— run on both the Lucene and analytics routes — then collapses that composite into aLogicalDedupviaPPLSimplifyDedupRule, so thatDedupPushdownRulecan push dedup into a Lucene scan.On the Lucene path, physical planning (Volcano) lowers the
LogicalDedupback (or pushes it into the scan), so it disappears. On the analytics path, the optimizedRelNodegoes straight to the analytics engine with no Volcano step, so theLogicalDedupsurvives to the engine's marking phase, which rejects it as an unmarked child.LogicalDedupexists solely to enable the Lucene pushdown (DedupPushdownRuleis its only consumer); the analytics engine has no equivalent pushdown and cannot plan the node.Fix
Add
CalciteToolsHelper.optimizeForAnalytics, which runs the same HEP rules asoptimizeminusPPLSimplifyDedupRule.RestUnifiedQueryActioncalls it on the analytics route, so the dedup composite is never collapsed and the analytics engine receives the standardROW_NUMBERwindow form it can plan directly.LogicalDedupand convert it back, the analytics route simply doesn't generate it — the rule that creates it (only useful for the Lucene pushdown) is skipped.optimizeis unchanged; the Lucene route keepsPPLSimplifyDedupRuleand its dedup pushdown.ScriptDetectorinspects the optimized plan), so worker-pool routing is unaffected.Testing
CalcitePPLDedupTest.testOptimizeForAnalyticsKeepsRowNumberForm(optimizeproducesLogicalDedup;optimizeForAnalyticskeepsROW_NUMBER, noLogicalDedup, predicate preserved)CalcitePPLDedupTest.testOptimizeForAnalyticsMatchesOptimizeWithoutDedup(dedup-free plan identical under both variants)CalcitePPLDedupTest(full class, regression)dedup category | fields category,dedup category | stats count(),dedup 2 category | fields categoryunmarked child [LogicalDedup]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.