Repository navigation
Price groupings from declared distinct counts of label sets (hierarchical labels) - #897
Merged
Merged
Conversation
A data workload can declare how many distinct combinations a set of dependent labels takes (distinct_values_per_label_set). A grouping over one source with no join below is priced at the smallest product of declared set and label counts covering its keys; the same bound gives G for whole-result targets. A set count above its labels' product is an input error naming the source.
…s unchanged labels Trace, per physical node, the output columns still holding one source row's labels; a relabeled key or labels a binary operator pairs fall back to the per-label counts. Search declared sets only over the keys they name. Clearer error and rejection texts.
…is bound by no declared set Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zzylol
added a commit
that referenced
this pull request
Oct 22, 2026
#897) into the per-node pricing
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.
Closes #894
Why
Hierarchical labels are over-counted. In the RollupHeavy evaluation workload, each of 625 endpoints belongs to one of 25 services. The data workload could only declare per-label counts, so the planner priced
GROUP BY service, endpointatmin(25 x 625, series_count) = 10,000groups instead of 625. At that size the cheapest plan for the 15-minute distinct-users query was a query-time raw scan: 1.8e9 rows per evaluation, 21.2 s predicted latency and 158 GB of raw data on disk.What
SourceFacts.distinct_values_per_label_set:series_count,rows_heldand the rows read, as before.bygroupings, partitioned sort/limit, sketch regroup and SQLGROUP BY;Gthat a whole-result accuracy target splits its failure probability δ over.(service, endpoint) 625. ThePlanningWorkloaddocs (input-output workflow, library API, accuracy models, the analytical-cost proposal) and the stage-viewer guide describe the field.How
SourceFacts::declared_distinct_combinations(labels)(types crate) computes the smallest covering product. It is a dynamic program over subsets of the keys that some declared set names; every other key contributes its own per-label count.group_count_boundcalls it when the grouping reads one source and every key is in that input'sunchanged_source_columns. That is a new per-node trace in the physical DAG of the output columns that still hold one source row's labels unchanged. Otherwise it uses the previous per-label product.Gpath:declared_group_boundnow traces all keys together (declared_distinct_combinations(node, columns, data), which replaces the per-columndeclared_distinct_values) down to the scans, and asks the source there.Key code interfaces added / modified
crates/types/src/workload/mod.rs
(
WorkloadErrorlists every variant in the source; only the changed ones are shown above, and none is removed.)crates/plan-selection/src/lib.rs (private functions, listed because they carry the algorithm)
tools/dag-viewer/stages.js, app.js:
dataWorkloadViewrows gaindistinctValuesPerLabelSet; the panel gains a column.Usage
This is the end-to-end run on the RollupHeavy component
L5/http-distinct-service_endpoint-15m, with a releasestage_pipelineat this PR's head, raw data retained.PlanningWorkload::validatechecks the set: 625 ≤ 25 x 625.SummaryAgg/PaneChainoverhttpreachesgroup_count_bound. Its keysservice, endpointare unchanged scan columns of one source, sodeclared_distinct_combinations(["service", "endpoint"])returns 625 (before this PR: 25 x 625 capped at 10,000).G = 625(declared_group_bound).The selected plan:
Before this PR, the same workload (without the set) selected a query-time scan:
scan 1800000000 samples; keeps 158400000000 bytes of raw data on disk, thenbuild Kmv into 10000 states, at 11.22 USD/30 d and 21,240 ms.Major algorithm changes
Group count from declared label sets (cost model 1 and the whole-result δ split)
Problem. A grouping's count was the product of its keys' per-label counts. With
service25 andendpoint625, where each endpoint is in one service,by (service, endpoint)was priced at 10,000 groups (the series cap) instead of 625. The planner then selected a raw-scan plan (above).Before.
group_count_bound:declared = Π_key Σ_source distinct_values_per_label[key].min(declared, Π series_count, rows_read).declared_group_bound(G): product over keys ofdeclared_distinct_values(node, key), each traced to the scans independently.After.
unchanged_source_columns(per physical node, in topological order) gives the output columns still holding one source row's labels:group_count_boundchecks whether the keys read one source and are all unchanged columns.declared = SourceFacts::declared_distinct_combinations(keys).declared_distinct_combinations(keys):in_sets) and the rest (alone).smallest[∅] = 1, andsmallest[m] = min over counts c with c ∩ m ≠ ∅ of smallest[m \ c] · count(c). The counts c are the declared sets and the single labels ofin_sets.smallest[in_sets] · Π_{alone} distinct_values_per_label.G:declared_distinct_combinations(node, columns)traces all keys together through the same unary operators to a scan. Union and concat branches add.Correctness.
declared_label_sets_bound_the_combinations_of_their_labels: 625; superset 4·625; no set → 25·625; subset of a set; uncovered key →None; 70 keys.a_label_set_count_is_at_most_its_labels_product: validation; the error names the source.a_grouping_is_priced_at_its_declared_label_set_count: by-groupings 625 / 2500 / 10,000; top-k partitions 625; alabel_join-relabeled key and agroup_leftbinary operator fall back to 10,000.values_per_result_is_bound_by_a_declared_label_set:G.a_declared_column_set_prices_a_sql_grouping_at_its_combinations: SQLGROUP BY.Complexity.
Assumptions and limits.
without (...)keeps its series bound, because its kept labels are known only at run time.Before this PR
The data workload could not express dependent labels. Every grouping combining
endpointwithservicewas priced at 10,000 groups. On the RollupHeavy components (raw data retained) that selected query-time raw scans: 21.2 s latency, 158 GB raw data on disk.After this PR
With
(service, endpoint)declared at 625, the groupings are priced at their true sizes. The raw scan disappears wherever the keys contain(service, endpoint)and no label without a covering set.L4/http-distinct-service_endpointtimes out at 3 min both before and after, as it did in the earlier evaluation run. Its two windows plan as the two L5 components above.Verification
cargo nextest run -p asap-types -p asap-plan-selection(425 passed);cargo nextest run -p asap-integration-tests -E 'binary(sql_group_bound) | binary(declared_group_counts) | binary(reuse_rules)'(17 passed).cargo test -p asap-planner --test sql_table_scans --test runtime_install(43 passed).cargo check --testson the frontends, planner and devtools.cargo fmt, andcargo clippy --all-targets -- -D warningson types, plan-selection and integration-tests: clean.python3 -m unittest test_viewer -k data_workload(5 passed).stage_pipelineat this head (default search, accuracy enforced, cost model 1 at AWS Fargate prices, 3-min timeout). Results are in the table above.