You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
DAG viewer: list the enumerated candidate DAGs and draw any of them #1006
The memo the planner builds for it on main (0f7465c96), measured with LogicalMemo::from_roots, try_populate_pass1 and the expanded_memo test helper:
Group
Logical sub-expression
Reads
G0
Scan data
–
G1
TimeRange 5m
G0
G2
per-series quantile 0.5
G1
G3
per-series quantile 0.5 (structurally equal to G2, but a separate group; see below)
G1
G4
G2 / G3
G2, G3
G5
per-series quantile 0.9
G1
G6
G4 + G5
G4, G5
G7
avg by (job)
G6
After
Groups
Implementation candidates
Full candidate DAGs
Stage 0
8
–
1
Stage 1, Pass 1
8
26 (G2, G3 and G5 have 7 each)
343 = 7 × 7 × 7
Pass 2 window composition + concrete state groups
378 (360 are PaneChain shared-build groups)
2614 (G2, G3 and G5 have 745 each: 13 Pass 1, 360 Panes, 372 SharedBuild)
> 1,000,000 (a naive enumerator stopped there)
G2 and G3 are separate groups because share_common_sub_dags (crates/types/src/ir/cse.rs) shares a non-ASAP node only when its schema has a provable unique key, and this per-series quantile's output schema has none.
The Pass 1 memo alone stands for 343 complete candidate DAGs. The viewer's Stage 3 list shows the selected plan and the rejections Stage 3 recorded under the default search. It can't show the other 342 DAGs, how many there are in all, or how each one differs from the selected plan.
Proposal
An Enumerated DAGs panel in tools/dag-viewer, reading memo.enumeration:
Header: the count, plus "≥" and the limit when complete is false.
One row per choice: for each group whose candidate differs from selected_choice, its candidate and origin; then its cost and validation result, when the DAGs were priced. A row with cheaper_than_selected is red.
Clicking a row composes nothing in the browser: the document already holds the composed DAG for the first --max-enumerated choices. The existing lanes draw it, the same as a Stage 3 row.
Scope and acceptance
stages.test.mjs covers the validation of memo.enumeration.
test_viewer.py checks, on the example above, that the panel says 343 for the Pass 1 tab and that clicking a row redraws the lanes.
Corrected 2026-10-28: production numbers from the stage-pipeline document; earlier numbers came from a test helper without the series-identity column.
The example in this issue was measured with asap_integration_tests::fixtures::lower_promql / expanded_memo. That lowering omits the PromQL series-identity column, so the per-series quantile had no unique key and the two quantile_over_time(0.5, data[5m]) stayed two groups (G2, G3). The production frontend adds $promql_series_identity, which gives the per-series output a unique key (series identity, timestamp), so they share one group. Production numbers, from tools/dag-viewer/examples/memo-ratio-of-quantiles.json (main at d728d05c):
Group
Logical sub-expression
Reads
G0
Scan data
–
G1
TimeRange 5m
G0
G2
per-series quantile 0.5 (both occurrences)
G1
G3
G2 / G2
G2 (both operands)
G4
per-series quantile 0.9
G1
G5
G3 + G4
G3, G4
G6
avg by (job)
G5
After
Groups
Implementation candidates
Candidate DAGs
Stage 0
7
–
1
Stage 1, Pass 1
7
19 (G2 and G4 have 7 each)
49 = 7 × 7 (16 use only budget_share 1)
Pass 2 window composition
7
259 (G2 and G4 have 127 each: 7 Pass 1, 120 Panes)
> 1,000 (enumeration limit)
Pass 2 rules + concrete state groups
354 (336 are PaneChain shared-build groups)
1738 (G2 and G4 have 691 each: 10 Pass 1, 336 Panes, 345 SharedBuild)
> 1,000 (enumeration limit)
For this issue: the Pass 1 memo stands for 49 candidate DAGs (the selected plan and 48 others), not 343.
Activity
zzylol commentedon Oct 28, 2026
Corrected 2026-10-28: production numbers from the stage-pipeline document; earlier numbers came from a test helper without the series-identity column.
The example in this issue was measured with
asap_integration_tests::fixtures::lower_promql/expanded_memo. That lowering omits the PromQL series-identity column, so the per-series quantile had no unique key and the twoquantile_over_time(0.5, data[5m])stayed two groups (G2, G3). The production frontend adds$promql_series_identity, which gives the per-series output a unique key (series identity, timestamp), so they share one group. Production numbers, fromtools/dag-viewer/examples/memo-ratio-of-quantiles.json(mainatd728d05c):Scan dataTimeRange 5mquantile 0.5(both occurrences)G2 / G2quantile 0.9G3 + G4avg by (job)budget_share1)Panes)PaneChainshared-build groups)Panes, 345SharedBuild)For this issue: the Pass 1 memo stands for 49 candidate DAGs (the selected plan and 48 others), not 343.