Repository navigation
[Refactor] Crate organization by stage: per-crate functionality, cross-stage code into asap-accuracy / asap-cost / asap-types, enforced crate boundaries, public vs private #1054
Description
Example
Run stage_pipeline on one query, quantile_over_time(0.9, latency[5m]), evaluated every minute with max_error 0.01. On main (cef05d2), the crate that should only select a plan (asap-plan-selection, Stage 3) runs every other stage itself:
| Step | Stage it belongs to | Where it runs today |
|---|---|---|
| hash-cons the roots | Stage 0 | plan-selection/src/search/pipeline.rs:197 → LogicalMemo::from_roots |
| per-computation candidates | Stage 1 Pass 1 | pipeline.rs:198 → generate_per_computation_candidates_with_memo; route.rs:872 → try_populate_pass1 |
| whole-result δ-sized candidates | Stage 1 Pass 1 | plan-selection/src/search/candidates/accuracy_configurations.rs:8 |
| pane candidates and shared summary states | Stage 1 Pass 2 | route.rs:889 → extend_local_window_composition; route.rs:906 → expand_concrete_summary_state_groups_before |
| physical candidates and materializations | Stage 2 | plan-selection/src/search/candidates/{inventory,configurations}.rs |
| realize each plan the search proposes | Stage 2 | search/refinement/planner.rs:1050 → physical.rs:82 → optimize_physical_asap_chosen |
| resize sketches for the failure probability and recompile the DAG | Stage 1 sizing + Stage 2 | plan-selection/src/lib.rs:2243 sized_for_failure_probability |
| check accuracy | Stage 3, but using Pass 1 code | lib.rs:275-284 imports 8 guarantee functions from pass1::per_computation_candidates |
The other crates have the same problem:
asap-typesholds algorithms used by one stage only.asap-physical-asap-optimization(Stage 2) depends on the Stage 1 crate for one field and one re-export.- Almost every module of
asap-logical-asap-optimizationispub.
What should happen instead:
- Each stage crate does only its stage.
- Stage crates never depend on each other.
- Shared models (accuracy, cost) and shared types live in non-stage crates.
- Each crate makes public only what other crates use.
Status (2026-10-31)
- code: decided 2026-10-31, Pass 1 sizes each approximate input for δ even when the query's root composes no guarantee (e.g. a ratio of quantiles under
avg, ortopk … or quantile). This follows the sizing table literally; such plans are rejected anyway when guarantees are enforced.
Done on refactor/stage-boundaries, apart from the open items below. Each stage crate depends only on asap-types, asap-accuracy and asap-cost. asap-plan-selection has no edge to logical or physical, and physical has none to logical. A CI test reads cargo metadata and checks the graph (integration-tests/tests/crate_graph.rs, #1132). The test allows two exceptions:
logical → frontend-promql, optional, enabled only bytest-support;planner → executor, which Deployment interface review: InstallablePlan, materialized views, DataStore and the executor #1016 decides.
Where the code differs from the text decided on 2026-10-29, the text below says so with "code:". Stage 3 internals are out of scope: the #1095 redesign was removed in #1133, so the old engine is Stage 3's only engine behind the frozen select_plan interface (#1047).
Open (Stages 0–2):
- Memo layout across crates ([Refactor] One named type per stage boundary: PerComputationLogicalMemoCandidates → LogicalASAPMemoCandidates → PhysicalASAPMemoCandidates #1047 D1).
MemoCandidatesis a public struct with public fields, and it is the publicmemofield of the Pass 1 and Pass 2 outputs. - Low-value visibility leftovers in
asap-types:validate_at_ingestionandplanned_data_statearepub, but only integration-tests use them.- Some IR compilation types (
PhysicalASAPNodeIdentityMap,PhysicalASAPDAGCompilation,ProducerStorageMetrics, …) have only test users outside the crate. Check each one against the public signatures before narrowing it.
- Naming leftover: Stage 0's hash-consing lives under the logical crate's
pass2/module (pass2/cse.rs,pass2/memo.rs). Optional.
Open, allowed but not needed yet (no work planned):
-
asap-costlower and upper cost bounds over all materializations, for early pruning in Stages 1–2. With them, cost-dominance pruning in Stages 1–2 would follow the pruning rule below.
Outside this issue: planner → executor and the deployment items in asap-types (#1016).
Concrete maintenance problem
Current crate graph ([dependencies], not dev-dependencies)
flowchart TD
planner["asap-planner<br/>(facade)"]
fsql["frontend-sql"]; fpromql["frontend-promql"]; fmql["frontend-metricsql"]; fcommon["frontend-common"]
ps["asap-plan-selection<br/>(Stage 3, plus S1/S2/facade code)"]
phys["asap-physical-asap-optimization<br/>(Stage 2)"]
logical["asap-logical-asap-optimization<br/>(S0 hash-cons, Pass 1, Pass 2, accuracy)"]
exec["asap-executor"]
types["asap-types<br/>(IR, workload, plus single-stage algorithms)"]
planner --> fsql & fpromql & fmql
planner --> ps
planner --> logical
planner -- "✗ #1016" --> exec
fsql & fpromql & fmql --> fcommon
fcommon --> types
ps -- "✗ X1, X3: calls Stage 2" --> phys
ps -- "✗ X2, X4: runs Pass 1/2, accuracy" --> logical
phys -- "✗ one field + one re-export" --> logical
logical --> types
phys --> types
ps --> types
exec --> types
classDef stage fill:#e8f0fe,stroke:#4a6fa5
classDef bad fill:#fdecea,stroke:#c0392b
class fsql,fpromql,fmql,fcommon,logical,phys stage
class ps bad
Edges marked ✗ are the ones this issue removes or redirects. Not drawn: devtools and integration-tests (they depend on almost everything); dev-dependencies (executor → logical, plan-selection); the optional logical → frontend-promql under test-support; external crates (asap_sketchlib, highs, parsers).
The graph has no cycles. The problem is which edges exist and what each crate holds:
| Edge | Why it exists |
|---|---|
| plan-selection → logical | Stage 3 runs Pass 1 and Pass 2 and uses Pass 1's accuracy functions (table above) |
| plan-selection → physical | Stage 3 realizes each proposed plan through Stage 2 (refinement/planner.rs:1050), and calls summary_materialization_eligibility while pricing (costing/direct_profile.rs:196) |
| physical → logical | only DeploymentCapabilities (one field, query_time_results_retained, physical/src/lib.rs:308) and pass1::realization::ranked_column, which merely re-exports asap_types::ir::schema::topk::ranked_column (realization.rs:172) |
| planner → executor | the executor's supported-summary list and the compile checks (installable.rs:380-390, :505-600); see #1016 |
What each crate holds today (production lines)
| Crate | Lines | Content, and what does not belong |
|---|---|---|
asap-types |
~15.5k | IR (operators, schema, scalars, guarantees, timing) and workload types. Single-stage algorithms: • ir/cse.rs (Stage 0 hash-consing)• ir/canonicalize.rs (frontends only)• physical/summary_window.rs plan_pane_phase / validate_pane_coverage and timing.rs split_shared_by_timing (Stage 2 only)• workload/resources/* (Stage 3 only)Deployment items: materialized_view.rs, SupportedSummary, agg_has_exact_per_entity_kernel |
asap-frontend-*, frontend-common |
~10k | Stage 0: query text → QueryRoot |
asap-logical-asap-optimization |
~15.4k | LogicalMemo hash-consing, Pass 1, Pass 2 (memo rules plus the whole-workload path), accuracy/.Of pass1/per_computation_candidates.rs (3,879 lines), only about 600 generate candidates (498-1110). About 2,000 lines are the shared accuracy model:• guarantee propagation (1615-3031) • guarantee helpers that Stage 3 calls (1328-1516) • sized (3661-3711)The rest is whole-workload composition and substitution. Pass 1 depends on Pass 2 ( pcc:613-625, :1690).DeploymentCapabilities is defined here but read by Stages 1, 2 and 3 |
asap-physical-asap-optimization |
~1.2k | Stage 2, plus the whole-workload optimize_physical_asap |
asap-plan-selection |
~23.4k | About 60% is Stage 3 (checks, pricing, cost models, MILP, report). The rest is: • Stage 2 catalog construction ( search/candidates/{inventory,configurations,physical,reconstruction}.rs, ~1.3k lines)• Pass 1 sizing and pruning ( query_pruning.rs, accuracy_configurations.rs, lib.rs:2243-2364)• facade orchestration ( search/pipeline.rs; search/route.rs except :954-1194)• the public API ( OptimizationPass, optimize, PlanningModels in search/mod.rs)• accuracy glue ( accuracy.rs, lib.rs:2651-2936) |
asap-planner |
~1.1k | Frontend entry, InstallablePlan. The selected plan's DAG is exported four times: Stage 2 (physical/src/lib.rs:664), Stage 3 after resizing (plan-selection/src/lib.rs:2260), and installable_plan_of before and after pane expansion (installable.rs:606, :622) |
Data structures and functions used across stages
| Item | Defined in | Used by |
|---|---|---|
IR: OperatorNode, QueryRoot, schema, AccuracyTarget, AccuracyGuarantee, Materialization, ExecutionTiming; workload types |
types | all stages |
| memo and stage-boundary types (#1047) | logical, plan-selection | two or more stages each |
accuracy model: accuracy/*, the guarantee propagation and sizing in per_computation_candidates.rs, AccuracyModel (plan-selection/src/accuracy.rs) |
logical, plan-selection | Pass 1, Pass 2, Stage 3 |
bounds derived from DataWorkload: rows, group count G, source rates (plan-selection/src/lib.rs:1852-2242, :2937-3211) |
plan-selection | pricing and accuracy sizing |
DeploymentCapabilities |
logical | Pass 1, Stage 2, Stage 3, planner |
CandidateGenerationTimeout |
logical | all stages |
PhysicalASAPDAG, compile_physical_asap_* |
types | Stage 2, Stage 3 (again), planner (twice), executor |
Visibility
asap-logical-asap-optimization:pub mod accuracy / pass1 / pass2and every submodule are public. About 40pubitems ofasap_aware_cseare used outside the crate only by devtools.asap-plan-selection:PlanSelectorand its methods,CalibrationFit,cost::analyticalandcost::operator_statistics(~20 types), and six cost constants arepubwith no user outside the crate.pub use search as pass;(lib.rs:297) gives one module two public paths.
asap-types: single-stage functions are public crate-wide.
Before and after structure
Decided 2026-10-29.
Target crate graph
flowchart TD
planner["asap-planner (facade)<br/>runs: frontends → S0 → P1 → per round: P2 → S2 → S3 → build plan → InstallablePlan<br/>splits deployment inputs; injects the Stage 2 realizer into Stage 3"]
fe["asap-frontend-*<br/>(Stage 0 lowering)"]
logical["asap-logical-asap-optimization<br/>(S0 hash-cons, Pass 1 incl. all sizing, Pass 2)"]
phys["asap-physical-asap-optimization<br/>(Stage 2, realizer)"]
ps["asap-plan-selection<br/>(Stage 3 only)"]
acc["asap-accuracy (new)<br/>guarantees, estimators, sizing, AccuracyModel"]
cost["asap-cost (new)<br/>resource usage, cost models 1/2/3, CostInputs"]
types["asap-types<br/>IR, workload, workload-derived bounds,<br/>stage-boundary types (asap_types::stages)"]
planner --> fe & logical & phys & ps
fe --> types
logical --> acc
logical -.->|early pruning| cost
phys -.-> acc
phys -.->|early pruning| cost
ps --> acc
ps --> cost
logical & phys & ps --> types
acc --> types
cost --> types
classDef stage fill:#e8f0fe,stroke:#4a6fa5
classDef shared fill:#e9f7ef,stroke:#2e8b57
class fe,logical,phys,ps stage
class acc,cost shared
Blue boxes are stage crates; there is no edge between any two of them. Green boxes are shared models; any stage may depend on them, and dashed edges are allowed but not needed yet. planner also depends on asap-accuracy, asap-cost and asap-types to assemble deployment inputs (not drawn).
Rules:
- Stage crates never depend on each other. The stage crates are
frontend-*,logical,physicalandplan-selection. A stage reads the previous stage's output type fromasap-typesand calls no other stage. Any stage may useasap-accuracyandasap-cost. asap-accuracyandasap-costdepend only onasap-types. Neither depends on the other: the workload-derived bounds both need live inasap-types.- The facade is the only crate that depends on every stage. devtools and integration-tests are unrestricted.
- CI checks the graph. A test reads
cargo metadataand fails on any[dependencies]edge outside the allowed list.
Per-crate functionality after the change
| Crate | Owns | Receives from |
|---|---|---|
asap-types |
IR, workload facts, workload-derived bounds (rows, group count G, source rates, confirmed_weights / with_confirmed_weights, #1083), the stage-boundary types of #1047 in asap_types::stages (DagOfLogicalMemoGroups, PerComputationLogicalMemoCandidates with ComputationTarget / FusedComputation / HavingEntry / HavingPlacement / HowToComputeAggregateMeasure (#1146), MemoCandidates, LogicalASAPMemoCandidates, PhysicalASAPMemoCandidates, PrunedCandidate / PruneReason, AssignmentRealizer, RealizedPlan), CandidateGenerationTimeout. code: EXACT_ACCUMULATOR_STATE_BYTES stays here, because the executor reads it and must not depend on asap-cost |
plan-selection (bounds), logical (memo types, timeout). Gives away its single-stage algorithms (below) |
asap-accuracy (new) |
Guarantee composition and propagation; estimators (target → sketch parameters); accuracy budget split; δ union bound; size_for_all_readers; merged-error checks; AccuracyModel, AccuracyEvidenceProvider, OperandDomain, satisfies |
logical accuracy/* and the accuracy parts of per_computation_candidates.rs; plan-selection accuracy.rs and lib.rs:2651-2936 |
asap-cost (new) |
(1) Resource-usage estimation: analytical model, operator statistics, sketch byte models, measured operation costs, calibration. (2) Cost models: cost model 1/2/3, ResourceUnitCost, Pareto, and CostInputs (was PlanSelectionSettings). It gives the usage for a given timing and materialization, and lower/upper bounds over all materializations, so earlier stages can prune by cost (code: the bounds are not written yet; see Status) |
plan-selection cost/*, lib.rs pricing (3212-4591) and byte models (4873-5184); types workload/resources/* |
asap-frontend-* |
Stage 0 lowering | types canonicalize, resolve_group_keys_promql |
asap-logical-asap-optimization |
Stage 0 hash-consing (generate_dag_of_logical_memo_groups); Pass 1, including all sizing (accuracy budget parts, whole-result δ, G); Pass 2 memo rules, including pane widths and shared summary-state sizes. code: DeploymentCapabilities is still defined here and re-exported by the planner; the facade splits it, so Stage 2 receives two bools and Stage 3 reads none of it |
types cse.rs; plan-selection accuracy_configurations.rs, resize_for_failure_probability, Pass 1 pruning |
asap-physical-asap-optimization |
Stage 2: physical candidates, allowed timing and materializations (recording may_keep_across_evaluations), pane phase and coverage, building the selected plan; the realizer used by Stage 3 (Stage2AssignmentRealizer). code: also the whole-workload optimize_physical_asap, compiled only with the reference-pricer feature (#1135) |
plan-selection search/candidates/{inventory,configurations,physical,reconstruction}.rs, per_entity_aggregate_violation (guess: executor legality); types summary_window.rs, split_shared_by_timing (#1145) |
asap-plan-selection |
Stage 3 only: checks (accuracy, latency, raw-data retention), MILP search, search-specific cost machinery (lower bounds, proposals, direct profiles, MILP price options), Selection |
— |
asap-planner |
Orchestration, including pane-width rounds (#1048) (run_planning, PlanningRun); public API (OptimizationPass, optimize, check_contract); deployment-input assembly and dispatch; InstallablePlan |
plan-selection search/pipeline.rs, the round loop of route.rs, search/mod.rs |
asap-devtools |
stage_pipeline, viewer data, fuzzing, and the reference pricer, kept to check the MILP's choice against enumeration (--price-enumerated, --exhaustive, planner_fuzz; #1002, #1004). code: the pricer's parts stay in their stage crates behind a reference-pricer feature (physical's optimize_physical_asap, plan-selection's whole-plan selector), and devtools enables it, so no production build compiles them (#1135) |
— |
Deployment items in asap-types (materialized_view.rs, SupportedSummary, the executor kernel list) and the planner → executor edge are decided in #1016.
Resolving the four cross-stage calls
| # | Today | After |
|---|---|---|
| X1 | Stage 3 calls Stage 2 for each proposed plan (refinement/planner.rs:1050): the MILP proposes with local legality and cost lower bounds; an evaluator realizes the plan through Stage 2, checks it and prices it exactly, and turns the result into a cut. The catalog does not carry combination-level legality (timing copies, pane phase), combined accuracy, or consumer-dependent cost |
Short term: the trait AssignmentRealizer lives in asap_types::stages. The Stage 2 crate implements it, and the facade passes that implementation to Stage 3. There is no crate edge, and physicalization stays lazy. Longer term (under consideration): Stage 2 precomputes the combination-level work, so realizing a plan is only assembling it from the catalog plus IR validation, and the realizer goes away. First measure how often and why Stage 2 rejects an assignment today |
| X2 | Stage 3 resizes sketches for δ and recompiles the DAG (lib.rs:2243-2364) |
All sizing is in Stage 1 (below); Stage 3 only checks |
| X3 | Pricing calls summary_materialization_eligibility (direct_profile.rs:196) |
Stage 2 records may_keep_across_evaluations on each configuration |
| X4 | Stage 3 imports 8 Pass 1 guarantee functions and the HLL/UnivMon estimators | They move to asap-accuracy |
Sizing: every size is decided in Stage 1, Stage 3 only checks
Sizes are decided in four places today:
- Pass 1 per budget share (
groups.rs:204-215→compose_alone→sized). add_scoped_candidatesin plan-selection.- Pass 2 shared sizes (
size_for_all_readers,shared_size, state groups). - Stage 3 δ resizing (
lib.rs:2243).
After the change:
| Decision | Stage | Function (asap-accuracy unless noted) |
|---|---|---|
Whole-window build size: ε, budget share n, δ split over G |
Pass 1 (absorbs places 2 and 4) | estimators, budget split, δ union bound |
Pane width w and count n = W / w, chain sharing |
Pass 2 window rule; the facade gives each round's width domain (#1048) | — (structural rule in logical) |
| Sketch size inside panes = the whole-window build's size | Pass 2 (inherited, never resized) | merged_error: Exact and AsIfBuiltOverUnion keep the guarantee at equal parameters; Approximate gives none |
| Shared build or chain size = the strictest reader | Pass 2 state groups | size_for_all_readers |
| Pane phase and alignment | Stage 2 | plan_pane_phase, validate_pane_coverage |
| Composed accuracy meets the target | Stage 3 (check only) | satisfies, guarantee composition |
Memory of n panes = n × sketch bytes |
Stage 3 pricing | asap-cost byte models |
Docs to fix (done: #1098 and the final docs pass #1148): planner-layering.md ("Summary nodes are created unsized") and per_computation_candidates.rs:22 should say that Pass 1 creates sized candidates, one per accuracy-budget split. Smaller sketches per pane are out of scope; they need a sketchlib bound that depends on the number of merges.
Pruning
#1047's rule becomes: a stage removes only what its own knowledge proves invalid, or what is provably dominated under the shared cost model. Each stage records PrunedCandidate<C> with one PruneReason enum (asap_types::stages). Cost-dominance pruning in Stages 1–2 is allowed but not implemented yet.
- Dominated means the pruned candidate's cost lower bound is ≥ the kept candidate's upper bound, for the same output, a guarantee at least as strong, and the same sharing.
- This is the memo design's "keep the cheapest per sharing signature".
Visibility rules
- Public: a crate's entry functions, its boundary and IR types, and what the facade and devtools use.
pub(crate): used by other modules of the same crate.- Private: used only in its own module.
#[cfg(test)]or atest-supportfeature: used only by tests.- No
pub modthat leaks internals, and no second public path to the same item.
| Crate | Change |
|---|---|
| logical | pass1, pass2, accuracy modules become pub(crate), with named pub use for the entry points and output types. asap_aware_cse items become pub(crate) (devtools labels move to the memo's origin). computation_candidates_for_intent, ClassicHllConfidence and DEFAULT_DELTA become private. Remove the realization::ranked_column re-export. code (#1145): every module is crate-private; the crate root re-exports what the planner and devtools use; other crates' tests reach internals through test_support (feature test-support) |
| plan-selection | PlanSelector (+ methods), CalibrationFit, cost::analytical, cost::operator_statistics and the six cost constants become pub(crate) (most move to asap-cost; #1136 narrowed asap-cost's own API). Keep one path (pass) instead of pub use search as pass. code: confirmed_weights and with_confirmed_weights moved to asap_types::workload::bounds and stay pub, since the planner and plan-selection call them in production |
| physical | optimize_physical_asap_assigned goes under test-support; physical_candidate_count and materializes_any go under #[cfg(test)]. code (#1135): catalog is private with root re-exports; no re-exports of asap-types items; test-only catalog items under test_support::catalog; materializes_any moved to asap-types with PhysicalWorkloadPlan, under cfg(any(test, feature = "test-support")) |
| types | Single-stage functions move to their stage. These become pub(crate): structural_hash, is_shareable_when_equal, SummaryCoverage::{derive, definition_id}, optimistic_floor, approximate_layer_count, expand_pane_chains, whole_window_build, compile_physical_asap_dag_with_node_ids, *_before, flatten_before, view_window. code: is_shareable_when_equal stays pub (logical's hash-consing calls it); the test-only ones are under test-support; split_shared_by_timing moved to physical (#1145), for which asap-types exposes timing_under_consumer and MaterializationAssignment::get |
Dead and test-only public code found by this review is listed in #1046.
Why the proposed structure is minimally complex
There are two new crates, each shared by several stages:
asap-accuracy: Pass 1, Pass 2, Stage 3.asap-cost: Stage 3 now; earlier stages for cost-based pruning later.
Everything else is moving code to the crate whose stage uses it, and narrowing visibility. Removing the edges between stage crates turns the "no calls across stages" rule (#1047) from a review convention into a compile error.
Existing unit and end-to-end protection
- Planner snapshots in
devtools/tests/corpus_snapshots/must stay unchanged in every PR. cargo test --workspace.- The new
cargo metadatagraph test.
Compatibility or migration impact
- Import paths change with no compatibility aliases (Reorganize by stage: one memo-based Pass 2, Stage 1 finishes the memo, deployment inputs split by stage, asap-planner as the only entry point #958 decision 5). Externally, ASAPQuery-backend uses only
asap_planner(InstallablePlan,PlanningWorkload,StoredOutputBuild,PlanError), checked in itsorigin/main. PlanSelectionSettingsbecomesCostInputs, including thestage_pipeline --plan-selection-settingsfile. code: the flag is--cost-inputs <file>and the planner fieldPlanningModels.cost_inputs(with_cost_inputs), with no alias (refactor: rename --plan-selection-settings to --cost-inputs and PlanningModels.plan_selection to cost_inputs (#1054) #1141).
Suggested order (one concern per PR; move PRs only move code):
- 1. Create
asap-accuracy; move the accuracy model (removes X4). refactor: create asap-accuracy with the accuracy model (#1054, part 1) #1077, refactor: move guarantee propagation and sizing into asap-accuracy (#1054, part 2) #1085 - 2. Create
asap-cost; move resource-usage estimation and the cost models. refactor: create asap-cost with resource-usage estimation and cost models (#1054) #1061, refactor: move Stage 3's cost formulas into asap-cost; PlanCost (#1095 C2) #1102 - 3. Move the single-stage algorithms out of
asap-types, and the workload-derived bounds into it. refactor: move single-stage algorithms out of asap-types (#1054) #1060, refactor: move data-workload bounds into asap-types (#1054) #1083, refactor(logical): narrow Stage 1's public API to what other crates use; split_shared_by_timing moves to Stage 2 (#1054) #1145 (split_shared_by_timing); refactor(accuracy): sized_for_failure_probability is private (#1054) #1131 made the deadsized_for_failure_probabilityprivate - 4. Move the boundary types into
asap_types::stages([Refactor] One named type per stage boundary: PerComputationLogicalMemoCandidates → LogicalASAPMemoCandidates → PhysicalASAPMemoCandidates #1047 P3–P5). The last two,DagOfLogicalMemoGroupsandPerComputationLogicalMemoCandidates, moved in refactor: Stage 0 and Pass 1 boundary types in asap_types::stages (#1054) #1146. The memo layout itself ([Refactor] One named type per stage boundary: PerComputationLogicalMemoCandidates → LogicalASAPMemoCandidates → PhysicalASAPMemoCandidates #1047 D1) is still open - 5. Move the Stage 2 catalog into
physical([Refactor] One named type per stage boundary: PerComputationLogicalMemoCandidates → LogicalASAPMemoCandidates → PhysicalASAPMemoCandidates #1047 P5); fix X3. Drop physical → logical. refactor: move the Stage 2 catalog into asap-physical-asap-optimization (#1047 P5b) #1090, refactor: Stage 2 records whether a pane chain may be kept; pricing reads it (#1054 X3) #1079, refactor: each stage reads only its deployment capability; physical no longer depends on logical (#958) #1075 - 6. Move all sizing into Pass 1 (removes X2), then the realizer trait (removes X1). refactor: decide every sketch size in Pass 1; Stage 3 only checks (#1054 X2) #1098, refactor: AssignmentRealizer trait in asap_types::stages, implemented by Stage 2 (#1054 X1) #1097. X1 is done in its short-term form; the long-term "realizer goes away" option stays under consideration
- 7. Move orchestration and the public API to the planner (Reorganize by stage: one memo-based Pass 2, Stage 1 finishes the memo, deployment inputs split by stage, asap-planner as the only entry point #958 step 6, [Refactor] One named type per stage boundary: PerComputationLogicalMemoCandidates → LogicalASAPMemoCandidates → PhysicalASAPMemoCandidates #1047 P7). Drop plan-selection → logical / physical. refactor: facade runs the stages; Stage 3's frozen select_plan interface (#1047 P7) #1107
- 8. Visibility pass per crate, then the CI graph test. types/frontends refactor: narrow physical, cost, accuracy and types visibility (#1054 step 8b) #1094, refactor: narrow asap-types test-only fields and frontend visibility (#1054 step 8b, part 2) #1099, fix: lower_metricsql and lower_metricsql_query stay public (CI external-consumer check) #1114; physical refactor(physical): one public path per Stage 2 item; reference pricer behind a feature (#1054) #1135; cost refactor(cost): narrow asap-cost's public API to what other crates use #1136; logical refactor(logical): narrow Stage 1's public API to what other crates use; split_shared_by_timing moves to Stage 2 (#1054) #1145; graph test test(integration): CI checks the workspace crate graph (#1054) #1132. Low-value leftovers in asap-types: see Status
Related
- Reorganize by stage: one memo-based Pass 2, Stage 1 finishes the memo, deployment inputs split by stage, asap-planner as the only entry point #958: Stage 1 crate reorganization; its decisions 3–4 are extended here.
- [Refactor] One named type per stage boundary: PerComputationLogicalMemoCandidates → LogicalASAPMemoCandidates → PhysicalASAPMemoCandidates #1047: stage-boundary types; [Refactor] Pane-width rounds without cross-stage calls: rerun Pass 2 per round, or pass an incremental Pass 2 state #1048: pane-width rounds.
- [Refactor] Rename memo-named caches and the memo types by stage #1050: memo and cache renames.
- Remove dead and test-only code: row-reference oracle, old_docs, test-only demand model, batch bridge, memo snapshot #1046: dead code.
- Deployment interface review: InstallablePlan, materialized views, DataStore and the executor #1016: deployment items and the executor dependency.
- Stage 1 Pass 2 review: ASAP-aware CSE rules over memo groups and summary-state groups #1013: Pass 2 window rules.
- memo-milp: size summaries once (final config per reader set and summary count; Stage 3 never resizes) #929: sizing once per reader set.
- Tracking: stage-by-stage review of ASAPPlanner — functionality, concepts, abstractions, interfaces #1010: stage-by-stage review.
Activity
refactor: move single-stage algorithms out of asap-types (#1054) (#1060)
refactor: create asap-cost with resource-usage estimation and cost mo…
zzylol commentedon Oct 29, 2026
Progress 2026-10-29:
asap-cost: done in refactor: create asap-cost with resource-usage estimation and cost models (#1054) #1061.PlanSelectionSettingsis renamedCostInputs;asap-costdepends only onasap-types.asap-types: done in refactor: move single-stage algorithms out of asap-types (#1054) #1060.canonicalizemoved to frontend-common, the pane layout and coverage code to physical, and CSE to logical.split_shared_by_timingstays inasap-typesbecause it reads private parts oftiming.rs. Theworkload/resourcesmove was done in refactor: create asap-cost with resource-usage estimation and cost models (#1054) #1061.asap-accuracy, after [Refactor] One named type per stage boundary: PerComputationLogicalMemoCandidates → LogicalASAPMemoCandidates → PhysicalASAPMemoCandidates #1047 P3asap_types::stages, with [Refactor] One named type per stage boundary: PerComputationLogicalMemoCandidates → LogicalASAPMemoCandidates → PhysicalASAPMemoCandidates #1047 P3–P5docs: every summary size is decided in Stage 1 (#1054) (#1062)
zzylol commentedon Oct 29, 2026
Decided 2026-10-29: the realizer is option D. A trait
AssignmentRealizerlives inasap_types::stages; the Stage 2 crate implements it, and the facade passes that implementation to Stage 3. The search algorithm is unchanged. The MILP algorithm itself may change later (an efficiency discussion is pending). Option B, where Stage 2 precomputes combination-level work so that realizing is only assembly, stays a candidate. Data point: in a profile of the 6 memo-example workloads, Stage 2 rejected no assignments in 5 of them; ratio-of-quantiles had 46 rejections, allno_accuracy_guarantee.refactor: narrow asap-types visibility to what other crates use (#1054)…
refactor: create asap-accuracy with the accuracy model (#1054, part 1) (
refactor: Stage 2 records whether a pane chain may be kept; pricing r…
refactor: split plan-selection lib.rs into modules by concern (#1054) (…
refactor: move data-workload bounds into asap-types (#1054) (#1083)
refactor: move guarantee propagation and sizing into asap-accuracy (#…
refactor: Stage 1 → Stage 2 boundary types in asap_types::stages (#1047…
refactor: narrow physical, cost, accuracy and types visibility (#1054 …
refactor: add AssignmentRealizer trait and Stage 2 implementation (#1054
refactor: decide every sketch size in Pass 1; Stage 3 only checks (#1054
refactor: narrow types test-only fields and frontend visibility (#1054 …
zzylol commentedon Oct 30, 2026
Progress:
main:AssignmentRealizertrait implemented by Stage 2.refactor/stage-boundaries(merges in refactor: stage boundaries milestone 1 — Stages 0–2 and Stage 3's frozen interface (#1047, #1054) #1108):asap-cost, plusPlanCost;refactor(accuracy): sized_for_failure_probability is private (#1054)
refactor(accuracy): sized_for_failure_probability is private (#1054) (#…
test(integration): CI checks the workspace crate graph (#1054) (#1132)
refactor(physical): one public path per Stage 2 item; reference price…
refactor(physical): one public path per Stage 2 item; reference price…
refactor: rename --plan-selection-settings to --cost-inputs and Plann…
refactor(logical): narrow Stage 1's public API to what other crates u…
refactor: Stage 0 and Pass 1 boundary types in asap_types::stages (#1054
docs: final docs pass for the Stages 0–2 refactor; fix every cargo do…
refactor: stage boundaries milestone 1 — Stages 0–2 and Stage 3's fro…