Repository navigation
feat(planner): error-budget allocation and whole-result δ scaling (#669, part b) - #712
Merged
Merged
Conversation
…aces the allocator hook (#669) accuracy/allocation.rs (AccuracyBudgetAllocator, EqualSplitAllocator, CompositionShape, AccuracyAllocation) is deleted. accuracy/budget.rs states the one split the planner makes, an equal share of an AccuracyTarget: eps/n (or (1+eps)^(1/n)-1 for a relative metric) and delta/n. The legacy replacement search calls it directly, and its constructors no longer take an allocator.
…uts at Stage 1 composition (#669) When a query's value composes several approximate inputs (every sketch summary below its root, when the composed guarantee is not exact), compose_logical_candidate sizes each for an equal share of the query's target: eps/n (relative: (1+eps)^(1/n)-1) and delta/n. Only inputs approximate in that candidate count; a summary several queries read takes the largest n. The summaries are first sized for the whole budget, then the candidate is composed again with the shares.
…lity sizing (#669) A per-value guarantee no longer meets a whole-result target directly. Plan selection states it over the G values one evaluation returns: delta_whole = G * delta_per (whole_result_guarantee). G is 1 for a scalar root and the declared input cardinality for a PromQL instant vector; a SQL relation or a range vector is unbound, which leaves a whole-result delta target unmet. A whole-result guarantee meets a per-value target directly, and the scope does not matter without a delta requirement or for a guarantee that never fails. Before pricing, Stage 3 enlarges each summary for delta / (n * G) of every query reading it (n approximate inputs): Count-Min and Count Sketch depth, KLL's failure probability, a distinct-count UnivMon's heap, and classic HLL's precision from its source's distinct bound. The guarantees above are composed again, the selected plan and the selected logical roots carry the enlarged sketches, and Stage 2's placement is kept. The legacy replacement search checks each node per value, as it sizes it. The PromQL stand-in of Example 2 states its targets per value: its declared input cardinality counts source IPs, not series.
G is 1 for a root that returns at most one row whatever its input, provable from the query: a global aggregate or summary (no group keys, not a top-k or count_values), and above it only operators that keep or drop rows, or joins and binary operations of two such inputs (not a full join). A grouped SQL root stays unbound.
This was referenced Oct 15, 2026
Contributor
Author
|
Planned simplifications of this PR's interfaces. They land in the follow-up PR after #717, together with the
|
This was referenced Oct 15, 2026
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.
Why
#669 composes guarantees through arithmetic and comparisons, but two parts were missing (part b):
count(a) - count(b), a summary over an approximate input), each input was sized for the query's whole budget. The composed error and failure probability could therefore be up tontimes the target.What
Equal budget split at Stage 1 composition. When a query's value combines
n ≥ 2approximate inputs, each summary is sized for an equal share of the query's target:ε/n(for a relative error,(1 + ε)^{1/n} − 1) andδ/n. Only inputs that are approximate in that candidate count. A summary several queries read takes the largestn. Stage 1 still reads no data workload.Whole-result scope. A per-value guarantee no longer meets a whole-result target directly. Stage 3 states it over the
Gvalues one evaluation returns, withδ_whole = G · δ_per(union bound). A whole-result guarantee meets a per-value target directly. Scope does not matter for a target without δ, or for a guarantee that never fails (δ = 0).G, the number of values one evaluation returns:
max_groups[source]input exists) and for a range vector. An unboundGleaves a whole-result δ target unmet, with its own rejection reason.Stage 3 enlarges summaries before pricing, so that cost reflects the larger sketch. Each summary is sized to fail with probability at most
δ / (n · G)for every query that reads it. The parameters enlarged are:kat the smaller δ (its δ scale);The selected plan (
Selection.plan) and the selected logical roots carry the enlarged sketches.accuracy/allocation.rsis deleted. That removesAccuracyBudgetAllocator,EqualSplitAllocator,CompositionShapeandAccuracyAllocation.accuracy/budget.rs(equal_share,equal_shares) replaces them onAccuracySpec. The legacy replacement search calls it directly, andASAPStrategies::new_with_planning_inputs*andHydraGroupingStrategy::new_with_planning_inputs_and_evidenceno longer take an allocator.How
compose_logical_candidatefirst composes each candidate with every summary sized for its whole budget. For each query whose guarantee is not exact, it then counts the sketch summary states below the root (approximate_inputs). If any query composes two or more, it composes the candidate again with each summary sized for its share.sized_for_failure_probabilityruns inselect_planbefore the accuracy check and pricing:estimators::with_failure_probability,hll::precision_for);with_resized_summaries; a node without a guarantee keeps none);whole_result_guaranteewraps the per-value δ inProbabilityExpr::Scaled { count: G }, or an unknownoutput_values_per_evaluationwhenGis unbound.satisfiesgains a scope check.Key code interfaces added / modified
crates/asap-aware-mapping/src/accuracy/budget.rs(new),lib.rscrates/asap-aware-mapping/src/accuracy/mod.rscrates/asap-aware-mapping/src/stage3_plan_selection.rscrates/asap-aware-mapping/src/replacement.rs,grouping.rscrates/asap-aware-mapping/src/accuracy/estimators/mod.rs,kll.rs,univmon.rs,hll.rscrates/asap-aware-mapping/src/per_computation_candidates.rsUsage, end to end (checked by running the planning stages at
a18fa4a6with a scratch test): a p99 panel over 10,000 series with a whole-result rank target.Before this PR
count by (job) (a) - count by (job) (b)with ε = 0.01, δ = 0.001: both Count-Min sketches were 272 × 7, sized for the full budget.quantile_over_time(0.99, x[5m])over 10 000 series with rank ε = 0.01, δ = 0.01: KLL was accepted with a per-value δ of 0.01 against a whole-result target.After this PR
The two Count-Min sketches are 544 × 8 each (ε/2 = 0.005, δ/2 = 0.0005).
The KLL is enlarged to fail with probability 1e-6 per series (δ/G), and that larger sketch is what gets priced and selected. A per-value target keeps the Stage 1 size.
docs: propose workload-wide planning, summary sharing, and materialization #509 examples (
stage_pipeline):No row of
docs/user_guide_docs/stage-viewer.mdchanges.Verification
accuracy::budget);per_computation_candidates);accuracy; this replaces the test that pinned the direct comparison);estimators);cargo nextest runfor asap-aware-mapping, asap-planner, asap-integration-tests, asap-devtools and asap-types: 1107 passed, 34 skipped. That includes feat(logical-asap-optimization): Example 2 — summary-capability rule and UnivMon frequency statistics #707'sstage3_plan_selection_accepts_sql_univmon_l2_beside_its_exact_population_count.cargo fmt --all -- --checkandcargo clippy --workspace --all-targets --all-features --locked -- -D warnings: clean.tools/verify_metricsql_external_consumer.sh: passes.Test changes
stage3_plan_selection_enlarges_hll_for_a_larger_distinct_bound.Limitations (all conservative: unmet, never wrongly accepted)
δ / (n · G)sizing does not cover that factor, so such a whole-result target can stay unmet.Selection.plancarry the enlarged ones.docs/design_docs/concepts/accuracy-models.mdstill describesAccuracyBudgetAllocator; doc updates are tracked in Update the docs after the G1–G11 consolidation groups land #677.Follow-ups
max_groups[source]input to bind G for grouped SQL results.