Skip to content

Stage 2 review: physical optimization and materialization per candidate #1014

Open
0 of 2 issues completed

Description

@zzylol

Part of #1010. Line numbers are on main at 958778400. phys/ = crates/physical-asap-optimization/src/, ps/ = crates/plan-selection/src/.

Example: what Stage 2 decides

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). After Pass 2 the memo has 354 groups and 1738 implementation candidates (#1008; corrected 2026-10-28: production numbers from the stage-pipeline document; earlier numbers came from a test helper without the series-identity column.) Stage 2 does not run as a pass over them. Stage 3 calls it per candidate while it builds its candidate graph:

  • implement_candidate_operators (phys/lib.rs:1103) turns a candidate's nodes into physical operators (for example exact top-k → Sort + Limit).
  • summary_materialization_eligibility (phys/lib.rs:261) says whether a summary build (a KLL SummaryAgg of G2, or one of the 336 PaneChains) may run at ingestion time, be kept across evaluations, or be materialized at query time.
  • Stage 3 then lists each node's (Materialization, ExecutionTiming) modes itself (ps/search/candidates/configurations.rs:162-222).

For the 1-minute [5m] KLL, the design's options are NotMaterialized, MatAtIngestion(Memory) (tumbling 1-minute panes maintained as data arrives) and MatAtQuery(Memory) when two or more queries read it; here one query reads it, so Stage 2's eligibility says no MatAtQuery (may_materialize_at_query: readers.len() > 1, phys/lib.rs:318), while Stage 3's mode list offers MatAtQuery to every summary (configurations.rs:163-167) and no production code reads may_materialize_at_query (only tests at phys/lib.rs:1674, :1688).

Functionality

  • Materialization: for each sub-DAG, not materialized / materialized at ingestion time / at query time (kept within one evaluation or across evaluations), and the medium; with the constraints "upstream of an ingestion-time node runs at ingestion time" and "stored while any reader needs it".
  • Physical operator implementation of each node.
  • (TODO in the design: parallelism, partitioning, resources.)

Concepts (design doc term ↔ code)

planner-layering.md Code on main
NotMaterialized, MatAtIngestion(medium), MatAtQuery(medium), kept across evaluations Materialization and Medium (crates/types/src/physical/materialization.rs:10, :21), with Materialization::timing() (:39)
CandidatePhysicalASAPDAGs / one physical candidate PhysicalWorkloadPlan (phys/lib.rs:133), which keeps materialization: BTreeMap<NodeId, Materialization> and assignment: Vec<Materialization>
materialization assignment also MaterializationAssignment (types/src/ir/properties/timing.rs:80, keyed by node pointer)
physical DAG PhysicalASAPDAG (types/src/ir/physical_export.rs)
eligibility SummaryMaterializationEligibility (phys/lib.rs:236), ingestion_eligibility, materialization_candidates (phys/lib.rs:766)

Interfaces: designed vs implemented

Designed (doc table) Implemented
Whole-workload one logical candidate + entries + DataWorkload → its physical candidates, one per materialization assignment optimize_physical_asap (phys/lib.rs:437) and variants (_assigned, _chosen, _without_query_time_raw_scans); production caller only query_pruning::alone_violation; otherwise devtools (planner_fuzz), integration tests and tests
Memo each implementation candidate, on demand → its physical operators and allowed materializations implement_candidate_operators, summary_materialization_eligibility, optimize_physical_asap_chosen, called from ps/search/candidates/{inventory,configurations,physical}.rs and ps/search/costing/direct_profile.rs
Deployment input query-time result retention DeploymentCapabilities.query_time_results_retained, read in phys/lib.rs and again in Stage 3 costing; phys depends on the Stage 1 crate only for DeploymentCapabilities

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 For every candidate for a memo group: implement its operators physically, list the allowed timing and materialization, record which candidates can supply each input and the reader-dependent legality, and prune. No pricing
Input &LogicalASAPMemoCandidates, entries, DataWorkload, query_time_results_retained (the Stage 2 part of DeploymentCapabilities), deadline
Output PhysicalASAPMemoCandidates { candidates: Vec<PhysicalCandidate>, query_roots, reader_constraints, pruned_candidates: Vec<PrunedCandidate<(CandidateForMemoGroupId, Materialization)>> }, plus methods implementation_roots(group) and query_output_candidates(output_reference) over a private index
PhysicalCandidate one operator node: candidate_for_memo_group: CandidateForMemoGroupId, is_implementation_root, physical_node, timing, materialization, input_suppliers, may_produce_query_output. No local_cost and no resource budgets (both move to Stage 3)
Function generate_physical_asap_memo_candidates(...) in asap-physical-asap-optimization, moved from plan-selection/src/search/candidates/{inventory,configurations}.rs; build_selected_physical_plan(&physical, &selection), called by the facade after Stage 3
PR #1047 P5 (P6 removes the whole-workload optimize_physical_asap caller)

Review checklist

User's suspected problem: over-abstraction / concepts defined twice in the physical stage

1. Boundaries

2. Naming

3. Stale code

4. "memo" / "cache"

5. Rules over memo groups

  • The doc says Stage 2 chooses a materialization "once for a group however many parents read it". Check that a state group (a shared KLL or PaneChain) gets one choice for all readers, and that a scan read through different windows may be read per reader.

Correctness and efficiency

Input/output check with the DAG viewer

Related issues

Sub-issues: #530, #654.

Referenced: #876 (labels, under #1008), #754 (kept-across-evaluations results rarely chosen; cost model, under #1015), #747 and #656 (PhysicalASAPDAG::validate, under #1016), #701 (operators without an executor kernel), #948 (naming), #958 (deployment inputs by stage).

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    optimizerWorkload optimization and plan selectionrefactor

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions