Conversation
This was referenced Oct 2, 2026
zzylol
force-pushed
the
stack/528-06-native
branch
from
October 2, 2026 18:32
75903b5 to
b22fa53
Compare
zzylol
force-pushed
the
stack/528-07-planner
branch
2 times, most recently
from
October 2, 2026 19:40
2a3bcd0 to
a03efc2
Compare
zzylol
force-pushed
the
stack/528-06-native
branch
from
October 2, 2026 19:40
b22fa53 to
d82bcde
Compare
zzylol
force-pushed
the
stack/528-07-planner
branch
from
October 2, 2026 21:14
a03efc2 to
2c708f3
Compare
zzylol
force-pushed
the
stack/528-06-native
branch
2 times, most recently
from
October 2, 2026 21:22
337a0ff to
a94bd59
Compare
zzylol
force-pushed
the
stack/528-07-planner
branch
from
October 2, 2026 21:22
2c708f3 to
03166e7
Compare
zzylol
force-pushed
the
stack/528-06-native
branch
from
October 2, 2026 21:25
a94bd59 to
d84830e
Compare
zzylol
force-pushed
the
stack/528-07-planner
branch
2 times, most recently
from
October 2, 2026 21:56
4b84314 to
d9da0f9
Compare
zzylol
force-pushed
the
stack/528-06-native
branch
from
October 2, 2026 21:56
d84830e to
b631137
Compare
zzylol
force-pushed
the
stack/528-07-planner
branch
from
October 3, 2026 02:31
d9da0f9 to
ec9f8cb
Compare
zzylol
force-pushed
the
stack/528-06-native
branch
from
October 3, 2026 02:31
b631137 to
075d8e2
Compare
zzylol
force-pushed
the
stack/528-06-native
branch
from
October 3, 2026 02:39
075d8e2 to
d9dc49c
Compare
zzylol
force-pushed
the
stack/528-07-planner
branch
from
October 3, 2026 02:39
ec9f8cb to
95eef55
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem: the workload planner still builds legacy
SummaryNodeplans, so #511's single operator DAG never reaches selection#511 (
operator-sharing.md, §Goal and problem) asks to "use one operator model before and after ASAP optimization, so ordinary query operations and summary operations can form one visible computation DAG". Its §3 Acceptance criteria require that:The companion #511 doc (
decoupling_op_and_expr.md, §2.3 and §4) adds that standalone scalar queries such as2ortime()need noPromqlScalarBridge. #509 (§Stages and their decisions) adds that "a candidate is a DAG for the whole workload, not for one query".The earlier layers of this stack (#537–#541) added the unified
OperatorNodeIR, its lowerers and the native compiler. They sit next to the production path. On the base branch (#541), workload planning still runs on the legacy types:SummaryExprrepeats ordinary operators under a second name (ValueOperation::{Project, Filter, Sort, Limit},RelationalJoin, a PromQLBinaryOp). It has no variant for set operations and the other ordinary operators. Any such operator has to be wrapped inKeepPreAsap(Rc<QueryExpr>), and a summary cannot appear below that wrapper. This is the "Today" tree from #511 §Goal and problem:Two more gaps:
ParsedWorkloadholds onlyRc<QueryExpr>, so the scalar is wrapped in a bridge node.PlanOutputreturns oneSummaryNoderoot per query. There is no single view of the workload DAG, and no way to see that two roots share an operator.This PR covers: the production cutover. Frontends,
ParsedWorkload, Pass 1 replacement search (ASAPStrategies), the existing Pass 2 sharing (share_common_sub_dags), selection, DAG assembly, lifecycle costing and the native compiler all useOperatorNodeandQueryRoot. It leaves out: new sharing rules,SummaryMergeplanning rules, ASAP replacement inside scalar-root sub-DAGs, and deleting the old source files.Proposed method
The pipeline keeps its stages. Only the type that flows between them changes.
lower_sql/lower_sql_dialectreturnRc<OperatorNode>.lower_promql_query_workloadreturnsVec<QueryRoot>, andlower_metricsql_queryreturns oneQueryRoot. A numeric literal query becomesQueryRoot::Scalar, with no bridge node.asap_planner::lowercollectsVec<QueryRoot>.ParsedWorkload::from_rootschecks that there is one root per workload entry. It then splits the roots into operator roots, remembering each one's entry index inoperator_indices, and(entry index, ScalarExpr)pairs.entries()yields only the operator entries.search_workload*runsasap_types::ir::cse::share_common_sub_dagsover the operator roots. It asserts that no root already contains an ASAP operator. For each target it asks the strategies.SketchAlgorithmStrategyis renamed toASAPStrategies. A candidate is now always anOperatorNodesub-DAG (Replacement::SubDAG):is_logical_rewritetells the two apart;retain_exact) returns the same sub-DAG withguarantee = exact("RetainedExact"). There is no wrapper node. A thread-local memo keyed by input pointer makes repeated calls return oneRc, so sharing is kept.OperatorNodebefore search and again after assembly (MajorPass). The second run turns identical summary producers chosen by different queries into oneRc, including producers that pre-ASAP CSE keeps apart (for example ungrouped aggregates, which have no unique key; seesummary_sharing.rs::identical_ungrouped_queries_share_their_producers).GlobalSelection::assemble_selected_daglinks per-target decisions into oneOperatorNodeDAG. A target with no chosen replacement keeps its own ordinary operator and assembles its children independently. SoProject,SetOp,Joinand other ordinary operators sit directly above summary nodes.assemble_selected_queryadds an exact-state read boundary (finalize_query_candidate) when a query root needs one.SummaryMaintenanceLifecyclePlan.rootand every lifecycle and cost API takeOperatorNode.PlanOutputkeeps oneQueryLifecyclePlanper operator entry. It also carries the scalar roots unchanged and exposes the whole workload DAG:roots()in entry order, andoperators()with each unique node once.check_contractcounts scalar roots, so entry indices stay0..n.Rc<OperatorNode>references from the transport DAG (physical_planner/logical.rs) before lowering. "Readout" is renamed to "evaluation" in the runtime (evaluationmodule,ExactEvaluation,ExactAccumulator::evaluation). The exact SUM state recordsseen, so an empty or all-NULL SQLSUMis NULL instead of0.Key code interfaces
crates/types/src/parsed_workload.rscrates/asap-aware-mapping/src/pass/mod.rscrates/asap-aware-mapping/src/replacement.rscrates/asap-aware-mapping/src/summary_maintenance_lifecycle.rsFrontends (
frontend-sql,frontend-promql,frontend-metricsql):Runtime (
crates/asap-physical-operators/src/evaluation.rs,summary_kernels/exact.rs):Usage (from
operator_design_examples.rs):Changed in the same way, by name only (the type moves from
QueryExpr/SummaryNodetoOperatorNode):ReplacementStrategy::propose_for_root,TargetSubDAG::{new, with_consumer_count},bindable_intent,RecurrenceProfileMap::for_target,GlobalSelection::for_target,CostModelhooks (summary_maintenance_lifecycle_cost_inputs,raw_query_recompute_cost,summary_support_evidence, …),dag_export::{export, export_summary},MaintainedPopulation/PopulationInput(generic parameter removed),CurrentSeriesInput::matches_node.ASAPStrategiesalso gains publicfixed_window_rate_candidatesandquery_time_rate_aggregation_candidates.Fields
ParsedWorkloadworkloadPlanningWorkloadexprsVec<Rc<OperatorNode>>operator_indicesVec<usize>exprs. Strictly increasing;entries()binary-searches it.scalarsVec<(usize, ScalarExpr)>new(workload, exprs)exprasQueryRoot::Operatorand callsfrom_roots.from_roots(workload, roots)LengthMismatchunlessroots.len()equals the number of entries. Set byasap_planner::e2e_plan.len()usizeexprs.len() + scalars.len().PlanOutputplansVec<QueryLifecyclePlan>plan.rootis anRc<OperatorNode>; roots can share nodes.scalar_rootsVec<(usize, ScalarExpr)>ParsedWorkload::scalar_rootsbyMajorPass. Not searched or rewritten.entry_indices()Vec<usize>check_contractrequires0..len().roots()Vec<QueryRoot>operator_roots()Vec<Rc<OperatorNode>>operators()Vec<Rc<OperatorNode>>roots(), including operators referenced by scalar expressions, each returned once (by pointer).len()usizeplans.len() + scalar_roots.len().Replacementand helpersSubDAGRc<OperatorNode>ExactCompositionExactCompositionis_logical_rewrite(node)booltruewhennode.guaranteeisNoneand the sub-DAG has no ASAP operator. Replaces the oldSummary/Rewritesplit. Used for evidence and runtime-support checks.retain_exact(expr)Result<Rc<OperatorNode>, _>expritself if it already has a guarantee or contains an ASAP operator. Otherwise returns a copy withResultGuarantee::exact("RetainedExact"), memoized per input pointer.ASAPStrategies<'a>planning_inputsCandidatePlanningInputs<'a>(private)default_cost_model,new,new_with_planning_inputsornew_with_planning_inputs_and_evidence. Behavior is the same asSketchAlgorithmStrategy.TargetSubDAG<'a>root&Rc<OperatorNode>consumer_countusizerootin the workload.newsets 1.search_workload_withcomputes it.strictest_sibling_accuracyOption<&AccuracyTarget>root's own. Set by search.CandidateLogicalASAPDAGs<Id>/GlobalSelectionrootsVec<(Id, Rc<OperatorNode>)>share_common_sub_dags. None may contain an ASAP operator (asserted).assemble_selected_dag(target)Result<Option<Rc<OperatorNode>>, _>Noneiftargetwas not discovered. Memoized per target, so a shared inner summary is oneRcacross roots.assemble_selected_query(target)assemble_selected_dagand thenfinalize_query_candidate, which adds aFinalizeExactAccumulatorread boundary atQueryTimewhere exact state reaches a query result. Use this for query results.search_workload(roots)CandidateLogicalASAPDAGs<Id>SummaryMaintenanceLifecyclePlan.root:Rc<OperatorNode>, the root of the assembled DAG being deployed. The other fields are unchanged.Frontends
lower_sql/lower_sql_dialectquery,catalog, (dialect),accuracyare unchanged. They return a resolvedRc<OperatorNode>whose schemas were derived during binding.lower_promql_query_workload(workload, now_ms)QueryRootper entry.now_msis the evaluation time used for lowering.…_with_histograms(workload, histograms, now_ms)HistogramCataloginstalled during lowering.lower_metricsql_query(query, accuracy)NumberLiteralquery returnsQueryRoot::Scalar. Everything else returnsQueryRoot::Operator.Runtime
exact_evaluation(states, statistic, range_ms, key)Result<Option<f64>, String>states(each must be anExactAccumulator; empty input is an error) and readsstatisticfor populationkey(None= unkeyed).range_msextrapolates Rate/Increase.Ok(None)means the population is absent: a counter with fewer than two samples, or an empty MIN/MAX.insufficient_counter_samples(state, statistic)booltrueonly for Rate/Increase on an exact state with too few samples.ExactEvaluation.statisticStatisticExactEvaluation.lookback_msOption<i64>ScalarState::Sum.seenbooltrueafter any update. Merge ORs it. A nullable unkeyed SUM output withseen = falseis emitted asValue::Null.Examples
End to end: two SQL queries share one SUM state
From
crates/integration-tests/tests/operator_design_examples.rs::batch_planning_replaces_and_shares_summary_operators.Input: a batch of two exact queries over
requests(bytes Float64), two invocations each, data at rest. The test cost model makes a summary build cost 1 and raw recompute cost 1000.What happens:
lowerreturns twoQueryRoot::Operatorroots. Each is aProjectoverSUM(bytes)overScan requests.ASAPStrategiesproposes an exact SUMSummaryAggfor theSUMtarget. Selection picks it over raw recompute.Projectas an ordinary operator above the summary. The exact-state read boundary sits between them.MajorPassrunsshare_common_sub_dagsover the assembled roots, so the two identical SUM producers become oneRc:Output checks:
entry_indices() == [0, 1],roots().len() == 2, exactly oneSummaryAgginoperators(), both roots reach it (Rc::ptr_eq),selected_raw_recompute == false. Each root compiles to the transport DAG, runs on rows[10.0, 20.0], and returns31.0and60.0.Cases covered by tests
UNION ALLof twoapprox_distinctSetOp { all: true }; each side holds aSummaryEstimateoperator_sharing.rs::each_side_of_union_all_holds_a_summary_estimateSELECT SUM(bytes) + 1 … WHERE status = 200, rewritten by handSummaryAgg+FinalizeExactAccumulator; JSON has noKeepPreAsap/ScalarBridgeoperator_design_examples.rs::sql_sum_projection_before_and_after_summary_rewrite11/NULL/NULLoperator_design_examples.rs::sql_sum_example_executes_with_sql_null_semanticslat[5m], same εRc, one deploymentplanner/tests/summary_sharing.rs::quantiles_with_equal_params_share_one_producersummary_sharing.rs::quantiles_share_one_producer_sized_for_the_strictest_consumer[5m]vs[10m]) or selector (job="a"vs"b")summary_sharing.rs::different_producers_are_not_sharedsummary_sharing.rs::sql_p50_and_p99_share_one_producerNOT INsubqueryScalarRefedgeoperator_design_examples.rs::sql_scalar_subquery_retains_its_cardinality_contractavgwith aProjectabove itAvghas no summary realization yetoperator_sharing.rs::project_above_and_scan_below_a_summary_are_both_non_asap_nodesavg+approx_percentile_contsharing oneScanoperator_sharing.rs::exact_aggregate_and_sketch_share_one_scanOut of scope
SummaryMergeplanning rules from docs: propose workload-wide planning, summary sharing, and materialization #509 §Pass 2.post_asap/{expr,cse,post_asap_dag}.rs,readout.rs, the frontends'unified/modules and similar files stay on disk but are no longer exported or compiled. The cleanup PR removes them. Duplicate transitional integration tests (unified_*) are removed here as their canonical counterparts take over.Stack and validation
Stack 7/8 · Previous: #541 · Next: #543 · Reference/tracker: #528
This is the main integration review. Review the planner behavior in
asap-aware-mappingandplanner, and theoperator_design_examples/operator_sharingintegration tests. That covers accuracy, costs, maintained-population lifecycles and scalar-root handling. The frontend and compiler changes promote implementations reviewed in the earlier layers to their canonical entry points. All compiled consumers migrate together.Validation: full workspace tests and doctests; workspace all-target check; formatting; workspace all-target/all-feature Clippy with warnings denied. Includes native execution of the document examples and real batch sharing.
🤖 Generated with Claude Code