Skip to content

Stage 1 Pass 2 review: ASAP-aware CSE rules over memo groups and summary-state groups #1013

Open
2 of 15 issues completed

Description

@zzylol

Part of #1010. Line numbers are on main at 958778400. loa/ = crates/logical-asap-optimization/src/.

Example: what Pass 2 produces

Same query as the tracking issue (avg by (job) (quantile_over_time(0.5, data[5m]) / quantile_over_time(0.5, data[5m]) + quantile_over_time(0.9, data[5m])), every minute). In the production planner, read from the stage-pipeline document tools/dag-viewer/examples/memo-ratio-of-quantiles.json (main at d728d05c):

Corrected 2026-10-28: production numbers from the stage-pipeline document; earlier numbers came from a test helper without the series-identity column.

After Memo groups Implementation candidates Candidate DAGs
Pass 1 7 19 (7 in each of G2, G4) 49
Pass 2 window composition 7 259 (G2, G4 have 127 each: 7 Pass1, 120 Panes) over 1,000 (enumeration limit)
Pass 2 rules + concrete summary-state groups 354 (336 are PaneChain shared-build groups) 1738 (G2, G4 have 691 each: 10 Pass1, 336 Panes, 345 SharedBuild) over 1,000 (enumeration limit)

The intended shape for this query, in the design's terms: G2 and G4 read the same data[5m] rows, so a summary-capability rule can add one shared KLL state group (one build sized for every reader) and a candidate in each of G2 and G4 that reads it, beside their own builds. A window-composition rule can add tumbling panes. Points to check against the measurement:

  • 336 PaneChain groups for one query (7 summary configurations × 48 pane layouts, pane widths 20 ms to 60 s; window composition alone offers 20 widths, 1 s to 60 s). add_local_window_composition uses PaneWidthDomain::Complete (loa/memo/window_candidates.rs:21-22), every divisor width; then a state group is made for every build that occurs at least twice, "including within one result" (loa/memo/state_groups.rs:148-160). One hypothesis (not confirmed): each pane width's chain appears in a composite candidate and in its factored readout, so each width becomes a state group.
  • 10 Pass1 candidates after Pass 2 vs 7 after Pass 1 (the 3 added are KLL candidates the state-configuration rule rewrote: one to the shared size k 1156, two keeping their size; one of those two, KLL k 1184, sized for half the budget, now has budget_share 1). rules/state_configurations.rs adds resized copies that keep their origin; to confirm.
  • expanded_memo expands with SeedGrids after offering Complete panes, while production uses one domain for both (plan-selection/src/search/route.rs, search_domain), and does not run accuracy_configurations::add_scoped_candidates. So the measurement is close to, not equal to, the production memo.

Functionality

  • Add candidates that share computation across sub-expressions and queries, keeping the independent candidates: identical expression (group identity), summary capability (one build, several estimates), window composition (tumbling panes, newest panes of a shared chain), rollup, exact top-k reuse, scan widening.
  • Make each shared configuration concrete: one summary-state group per shared size or pane layout, read by the candidates that can use it.

Concepts (design doc term ↔ code)

planner-layering.md Code on main
Memo group: one distinct logical sub-expression MemoGroup (loa/memo.rs:49); group identity = same logical result (hash-consing)
ASAP memo group: same summary state a "concrete summary-state group": a new MemoGroup whose node is the SummaryAgg / PaneChain, one SharedBuild candidate (state_groups.rs:148-185), result StateGroupExpansion (state_groups.rs:19)
Implementation candidate (with coupling sites and allowed materializations) ImplementationCandidate { implements, node, children, origin, budget_share } (loa/memo/candidates.rs:24): no coupling-site or materialization field
Coupling site (CouplingSite, CouplingKind) does not exist; each threshold becomes its own state group. Raw horizon is a Stage 3 MILP term (raw_retention, plan-selection/src/search/milp/direct_cost.rs)
Which rule made a candidate CandidateOrigin (candidates.rs:47): Pass1, SharedBuild, SummaryResult, SummaryInput, Panes, Rollup, TopKReuse, WidenedScans (4 of these have no doc name)

Rule ↔ code (memo path, run in this fixed order by expand_with_state_index, state_groups.rs:102-111; window composition runs before it from Stage 3):

Doc rule Memo code Adds
Identical expression hash-consing (memo.rs, types/src/ir/cse.rs) nothing
Window composition extend_local_window_composition (window_candidates.rs:46), calls pane_candidates / window_composition_pass2_candidates from asap_aware_cse.rs Panes
Scan widening rules/scan_labels.rs (wraps with_union_scan_labels) WidenedScans
– rules/summary_inputs.rs, rules/summary_results.rs SummaryInput, SummaryResult groups
Exact top-k reuse rules/topk.rs (wraps topk_reuse_candidates) TopKReuse
– (SQL finer aggregate, #950) rules/finer_aggregate.rs + loa/finer_aggregate.rs –
Rollup rules/rollup.rs (wraps rollup_candidate_for, rollup_pass2_candidates) Rollup
Summary capability, pane chain sizes rules/state_configurations.rs via the SharedProducer trait (rules/shared_producer.rs), coverings from memo/state_configurations.rs resized / re-paned copies, then state groups

Interfaces: designed vs implemented

Designed Implemented
Input CandidateMemo; entries' recurrence and range steps same
Output complete memo: each rule's candidates added, with coupling sites expand_concrete_summary_state_groups_before(CandidateMemo, PaneWidthDomain, deadline) -> StateGroupExpansion (state_groups.rs:75)
Who runs it Stage 1 (doc: "a planned change moves this expansion into Stage 1") Stage 3: search_domain in plan-selection/src/search/route.rs runs try_populate_pass1, add_scoped_candidates, extend_local_window_composition, then the expansion, once per pane-width round
Whole-workload Pass 2 being deleted (#958) still present: Pass2Candidate (loa/workload_candidates.rs:32), stage1_candidates_of_choice (:68) and the *_pass2_candidates subset enumerations in asap_aware_cse.rs (4,649 lines, tests from about :2998)

Interface decided in #1047 (2026-10-29)

Decided in #1047, the stage-boundary types. Its sub-issue is #1048. Names follow #1050 within #1047's scope. Principles: a stage or pass reads only the previous step's output type and calls no other stage; the asap-planner facade runs the stages in order; each stage prunes only what its own knowledge rules out and records it as PrunedCandidate<C> { group, candidate, reason: PruneReason }; queries are entry indices. Planning stays as fast as on main: P6 and P7 report before/after stage_pipeline runs.

Decided
Functionality Only adds groups and candidates; prunes nothing
Input &PerComputationLogicalMemoCandidates (borrowed; the facade reruns Pass 2 per pane-width round, #1048), entries, PaneWidthDomain, deadline
Output LogicalASAPMemoCandidates { memo (private), query_roots }. StateGroupExpansion is deleted; a shared-state group is recognized by its node (SummaryAgg / PaneChain)
Function generate_logical_asap_memo_candidates(...), the single entry. It calls each rule explicitly, in order: 1 local window composition, 2 scan widening, 3 summary inputs, 4 top-k reuse, 5 finer aggregate, 6 rollup, 7 window rollup, 8 summary results, 9 shared configurations, 10 add_shared_summary_state_groups (only the last step; was expand_concrete_summary_state_groups*, which hid rules 2–9)
Pane-width rounds The facade runs them (Pass 2 → Stage 2 → Stage 3 per round). Stateless Pass 2 per round first, measured; an incremental state only if needed (#1048)
Legacy path #1047 P6b deletes workload_candidates.rs, Pass2Candidate and the whole-workload *_pass2_candidates entries once P6 removes prune_query_candidates. The asap_aware_cse helpers the memo rules call (pane_candidates, window_composition_*, rollup_*, topk_reuse_candidates, with_pane_chain, with_resized_build, with_union_scan_labels) stay; rewriting them as native memo rules is this issue's and #958's work
PR #1047 P4, P6b, P7

Review checklist

User's suspected problems

1. Boundaries

2. Naming and over-abstraction

3. Stale code and stale docs

  • planner-layering.md lines 283-287 give the memo's code interface as crates/plan-selection/src/search/memo/ with Memo, CouplingSite, CouplingKind, brute_force::memo, brute_force::brute_force and Stage1ToStage3Search::memo_groups. None exist on main; the memo is in loa/memo*. Line 252 says the memo search is "not yet the default"; it is the only route (run_stage1_to_stage3_from_pass1, plan-selection/src/search/pipeline.rs:195).
  • Whole-workload subset enumeration (identical_expression_pass2_candidates, summary_capability_pass2_candidates, topk_reuse_pass2_candidates, caps of 20 such as MAX_CAPABILITY_SHARING_MEMBERS) is reachable only from workload_candidates.rs, query_pruning.rs, devtools and tests. [Refactor] One named type per stage boundary: PerComputationLogicalMemoCandidates → LogicalASAPMemoCandidates → PhysicalASAPMemoCandidates #1047 P6b deletes them once query_pruning.rs is gone (P6).

4. "memo" / "cache"

5. Rules over memo groups

Correctness and efficiency

  • Explain the 336 pane-chain groups and 691 candidates per quantile group for one query; decide which are intended (the memo, not the MILP, should bound this).
  • The memo path has no candidate caps (memo/state_configurations.rs, Complete uses every millisecond divisor); only deadlines and pane-width rounds bound it.

Input/output check with the DAG viewer

Related issues

Sub-issues: #958, #954, #951, #997, #731, #522, #873, #891, #903, #278, #580, #623, #464, #463.

Referenced: #979 (group identity, under #1011), #929 (size once, under #1015), #1002 / #1003 / #1005 / #1006 (memo tooling, under #1008), #948 (naming).

🤖 Generated with Claude Code

Activity

  1. added
    optimizerWorkload optimization and plan selection
    windowWindow functions, frames, panes, or temporal semantics
    on Oct 28, 2026
  2. zzylol commented on Oct 29, 2026

    @zzylol
    ContributorAuthor

    Window sizing, decided 2026-10-29 in #1054 under the rule "every size is decided in Stage 1; Stage 3 only checks":

    • Pane width w and count n = W / w. A structural choice made by the Pass 2 window rule. The default is w = gcd(W, every reader's evaluation grid) (asap_aware_cse.rs:1170). Other widths come from PaneWidthDomain, whose domain for each round the facade passes in ([Refactor] Pane-width rounds without cross-stage calls: rerun Pass 2 per round, or pass an incremental Pass 2 state #1048). Each width and each way of sharing a chain becomes a candidate.
    • Sketch size inside panes. It is inherited from the whole-window build's Pass 1 size and never resized. This is valid because FamilyCapability::merged_error is Exact or AsIfBuiltOverUnion, meaning that at equal parameters the merge adds no error. With Approximate, the merged readout has no guarantee and Stage 3 rejects the plan for approximate targets. The δ resizing that Stage 3 does today (plan-selection/src/lib.rs:2243) moves into Pass 1, so pane chains inherit the final size.
    • Shared chain or build. It takes the strictest reader's size (size_for_all_readers), with one state group per distinct size.
    • Pane phase and alignment. Stage 2 (plan_pane_phase, validate_pane_coverage).

    For this issue:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    CSEoptimizerWorkload optimization and plan selectionrefactorwindowWindow functions, frames, panes, or temporal semantics

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions