Skip to content

Tracking: implement the optimization formulation (memo candidates, properties, edges y_{u,c,i}, MILP) as named code per stage #1207

Open
0 of 8 issues completed

Description

@zzylol

Problem

Query A: quantile_over_time(0.95, latency[1m]) with a rank-error target 0.01. Query B: quantile_over_time(0.99, latency[1m]) / quantile_over_time(0.50, latency[1m]) with a relative-error target 0.05.

In the formulation below, the plan is stated in a few objects:

  • State groups. A KLL state group holds candidates with sizes from a predefined list, e.g. k=200 and k=345; a DDSketch state group holds alpha=0.01 and alpha=0.02. Each candidate has a guarantee derived from its parameters, a timing and a lifetime.
  • Edges. A reads the KLL state (rank error). B's two operands read the DDSketch state (relative value error): rank error does not bound a ratio's relative error, so KLL edges into B's operands do not exist (metric rule). Accuracy is checked per query at its root (constraint (K)): A's error within 0.01, and B's composed ratio error within 0.05.
  • Stage 3 choice. B's two operands may read one DDSketch candidate $u$: $x_u = 1$, two $y_{u,c,i} = 1$, and its cost is paid once.

In the code today, none of these is a value you can point at:

  • Size. k lives inside each candidate's operator node, sized for a fixed share of one query's budget (accuracy_budget_parts).
  • Timing and lifetime. They are folded into one Materialization enum.
  • Edges. There are no edge objects; Stage 2 stores per-input producer sets filtered by timing only.
  • Edge variables. Stage 3 has no $y$; an edge is implied by two choices in one execution context.
  • Sharing. It comes from extra SharedBuild candidates and per-size state groups that Pass 2 copies into the memo.

This tracking issue makes each notation and variable of the formulation a named type, field or function in the code, stage by stage. Where the code must differ, the formulation says so.

Representation, rules, enumeration (after this refactor)

  • (a) Representation: what is stored.
    • A memo of groups. A group $G$ is one logical result, or one state family, over specified input data. Groups are hash-consed, so an identical subexpression of two queries is one group.
    • A candidate of a group is $v = (e_v, p_v)$: one operator over child groups, plus properties. The logical property is $\theta$ (summary parameters from a predefined list, with precomputed $\varepsilon$, $\delta$). The physical property is (timing, lifetime).
    • Sizes and physical properties are properties of a candidate, not part of a group's identity. So a mergeable state with several sizes is one group with several candidates.
    • Explicit producer–consumer edges $(u, c, i)$, each with $\alpha^{\log}$ and $\alpha^{\mathrm{phys}}$.
    • The memo stores the set of possible DAGs compactly: a DAG is one producer choice per (consumer, input), starting from each query's root group.
  • (b) Rules: how the stored set is extended.
  • (c) Enumeration: how a DAG is drawn from it.
    • DAGs are not listed one by one. The Stage 3 MILP picks $x_v$ (candidate used) and $y_{u,c,i}$ (edge used), with these constraints:
      • every used consumer input picks exactly one producer that satisfies $\alpha^{\log}$ and $\alpha^{\mathrm{phys}}$;
      • a producer is used whenever an edge reads it;
      • each query's root group is covered;
      • acyclic;
      • accuracy per query at its root (K), retention (R), latency (L), resource budgets (B);
      • minimize $\sum w_v x_v$.
    • The chosen DAG is ${v : x_v = 1}$ with edges ${y = 1}$. A producer read by several edges is shared, and its cost is counted once.
    • On small workloads, a reference enumerator (one producer per consumer input) checks the MILP's answer.

Each sub-issue states which of (a), (b), (c) it changes.

Notation → code, and which sub-issue owns it

Notation Meaning Code today Sub-issue
$G$ memo group MemoGroup (stages/memo.rs): its expression and group_properties (GroupProperties: schema, result kind, summary coverage over a SummaryStateDefinition, shareable), no per-size state groups #1203
$v = (e_v, p_v)$ candidate CandidateForMemoGroup (stages/candidates.rs:25) #1203
$e_v = \operatorname{op}v(G{v,1..n})$ expression root_node + input_groups #1203
$p^{\log}_v = (\theta_v, \omega_v)$ summary parameters, window layout inside root_node's operator arguments; accuracy_budget_parts #1203
$\varepsilon_v$, $\delta_v$ precomputed error bound and failure probability of $\theta_v$ (from the predefined list) computed per budget share by Pass 1 sizing #1203
(K) per query: $\sum \lambda_{q,u}\varepsilon_u a_{q,u} \le \varepsilon_q$, $\sum \delta_u a_{q,u} \le \delta_q$ #1144's constraint 8 with N[q], A[q, parts] #1204
$g_c$, $\lambda_{q,c,i}$, $\text{cap}_{c,i}$ error propagation per consumer; propagation factor to query $q$, read from $q$'s expression; per-input error cap = min(validity cap, pruning cap), which sets the parameter floor $\theta^{\min}_{c,i}$ not represented; accuracy_budget_parts #1203, #1204
$p^{\mathrm{phys}}_v = (\tau_v, \ell_v)$ timing, lifetime PhysicalCandidate::{timing, materialization}; Materialization mixes both #1200
execution context $\kappa$ where a candidate with $\ell$ = None or OneEvaluation runs ($\kappa_T$: one per (QueryRecurrence, RangeEvaluation), shared by the queries with equal ones; $I_r$; $\kappa_m$); only these candidates are indexed by it. A consumer in $\kappa$ reads a tumbling chain of width $w$ iff $w$ divides every evaluation time of $\kappa$ (delivers_on) p[ctx, c] for every candidate #1204
$r_{c,i} = (r^{\log}, r^{\mathrm{phys}})$ input requirement; $r^{\log}$ = LogicalRequirements { parameter_floor_for_required_accuracy }; $r^{\mathrm{phys}}$ is a function of the consumer's own $p^{\mathrm{phys}}_c$, not stored $r^{\log}$ absent; $r^{\mathrm{phys}}$ is the consumer's timing #1203, #1200
$\alpha^{\log}$, $\alpha^{\mathrm{phys}}$ satisfaction $\alpha^{\log}$ absent; $\alpha^{\mathrm{phys}}$ = compatible_timing #1203, #1200
$P_{c,i}$, $\mathcal E$ candidate edges producer_candidates_per_input (interned sets; no edge ids; rejected edges dropped) #1201
state group $G^s$ a mergeable state as its own group; size is a candidate's $\theta$ (not part of the group's identity) private node of each consumer; consumer copies per tumbling-window layout; SharedBuild and per-size groups #1203 (tumbling-window builds as its candidates: #1160)
$\mathcal C$ physical candidates PhysicalASAPMemoCandidates::candidates, one per (operator node, timing, materialization) #1200, #1201
$x_v$, $y_{u,c,i}$, $z_{q,v}$, $t_v$ MILP variables p[ctx,c], x[m], no y (implicit p ∧ p, plus e binaries), constraint 1, rank #1204
(A)–(I) constraints #1144's constraints 1–9 #1204
$w_v$ cost: fixed part $w^{\text{fixed}}_v$ plus retention $\pi_v h_v$ per-context terms in cost_terms.rs #1204
$h_v$ retention amount of a materialized candidate, $h_v \ge \text{need}{c,i}, y{v,c,i}$ (constraint R) $S[m]$, $H[m]$, $\pi[m,\ell]$ #1204
printing all of the above check feasibility of each edge memo panel, --text (#1159); no rejected edges; no Stage 3 pruned list #1205
window layouts tumbling / sliding-merge / sliding-subtract candidates only tumbling (SummaryTumblingWindowChain) #1206 (future)

Sub-issues

Each sub-issue has the same structure: the formulation for its scope, the data structures and interfaces to change per stage, and a table of today's code against what it should become.

Policy: refactors delete what they replace (decided 2026-11-04)

Interfaces (decided 2026-11-05). The data structures and interfaces of each stage are the ones defined in this issue, its sub-issues, and the concepts doc memo-groups-properties-requirements.md, section 3 (#1224). Each PR:

These PRs are refactors toward the formulation. Each one:

  • deletes the old implementation it replaces in the same PR: no old code path kept beside the new one, no feature flag or environment switch to fall back, no compatibility shim, wrapper or alias of an old type or name;

  • leaves no dead code: every type, field, function, variant and test helper the PR makes unused is deleted, and the PR body lists them;

  • does not reuse an old or stale abstraction for a new concept. For example:

    • lifetime is a new Lifetime, not Materialization renamed;
    • an edge is a PhysicalEdge, not ProducerCandidates re-documented;
    • a context is not a ResultSlot.
  • models every property and requirement as a product of dimensions (decided 2026-11-05): one field per dimension, each its own type, with a checked constructor that rejects the invalid combinations. Never a flat enum that lists the combinations.

    • $p^{\mathrm{phys}}$ = PhysicalProperties { timing, lifetime };
    • $p^{\log}$ = LogicalProperties { parameters, window_layout };
    • $r^{\log}$ is LogicalRequirements { parameter_floor_for_required_accuracy }; $r^{\mathrm{phys}}$ is a function of the consumer's own $p^{\mathrm{phys}}$, not a stored struct.

    A dimension may carry data that only some of its values need, e.g. Lifetime::OneEvaluation(StorageMedium).

  • changes producers and consumers of a changed format together: export and backend, viewer documents, snapshots. No code that parses the old format is kept.

  • Terminology (decided 2026-11-05). Consumer c is the candidate reading an input; query q (or its execution context κ) is who needs a result on a schedule 𝒯_q. Issues, the doc and new code do not use "reader".

Order (rewritten 2026-11-06: interfaces first, baseline before extensions)

Source of truth for every interface: the concepts doc docs/design_docs/concepts/memo-groups-properties-requirements.md (#1224). PR 0 defines every data structure and interface of the doc and deletes the old ones they replace; each later PR fills in one part. No PR adds a temporary abstraction. Tests may fail until the PR that makes them pass; each PR says which.

PR Content Doc Tests passing after it
0. Interfaces Every type and signature of the doc (Sections 3–5), bodies todo!(). Deletes what they replace: Materialization and OperatorNode's physical fields, root_node and private nodes, input_groups, accuracy_budget_parts, SharedBuild and SummaryResult, rule 9, step 10 and per-size groups, DagOfLogicalMemoGroups (one Memo), ProducerCandidates as the interface, compatible_timing, all of ReaderConstraint, ResultSlot, slot_of, the e binaries, z[m, G], N[q], A[q, parts], #1144's constraint 8, may_keep_across_evaluations_per_sole_reader_query 3, 4, 5 it compiles
0b. One crate per stage Move-only, no logic changes. Each stage and pass is its own crate: asap-stage0-memo (hash-consing into the Memo), asap-stage1-pass1-per-computation-candidates (initial candidates), asap-stage1-pass2-reuse-candidates (reuse rules and logical edges), asap-stage2-physical-optimization (physical candidates and edges, today asap-physical-asap-optimization), asap-stage3-plan-selection (selection, today asap-plan-selection); asap-logical-asap-optimization is deleted. As in #1054: stage crates never depend on each other (each reads the previous stage's output through asap-types), shared code lives in asap-types, asap-accuracy or asap-cost, each crate exposes only what other crates use, and the crate-graph test (integration-tests/tests/crate_graph.rs) checks the edges. The facade calls the stages in order — same as PR 0
1. Properties and requirements PhysicalProperties::new and its invalid combination; parameter_list, LogicalProperties::new, LogicalRequirements, producer_satisfies_logical_requirements, producer_satisfies_physical_requirements, delivers_on 2 types
2. Memo Stage 0 hash-consing; Memo::{find_or_add_group, add_candidate, add_query, candidate} (returning Result; CandidateError); groups as expressions with GroupProperties derived from their input groups by the existing rules read through OperatorInput (MemoGroupAsInput, derive_group_properties), never node trees (MemoGroup { expression, group_properties, … }), private behind groups(); summary coverage only in the memo (SummaryStateDefinition); candidates inside MemoGroup 3 (Stage 0) Stage 0
2b. Column lineage instead of table qualifiers GroupProperties::column_lineage: Vec<ColumnLineage> (each output column is ColumnLineage::SourceColumn(SourceColumn { source, column }) or ColumnLineage::Computed), derived from the input groups' lineage; summary coverage keys selection columns by the same SourceColumn and does not lift a predicate on a Computed column; Schema fields lose their table qualifier; front ends resolve qualified names with their own scope while lowering; the qualifier patches (with_aggregate_qualifiers, requalification in OperatorNode) are deleted; add_candidate compares schemas exactly, with no qualifier to ignore. Export and backend wire format change with PR 6 3 (Stage 0/1) types, frontends
3. Pass 1 exact, accumulator, summary (one candidate per θ), fused; intermediate results as groups. Re-creates Pass 1's test coverage deleted in PR 0b (target discovery, HAVING placement, candidate composition, fused heaps, whole-expression top-k, nested-sum merges, ranked items, raw-data signs, guarantee composition, stream norms), with tests that build their input Memo through Memo::find_or_add_group and add_candidate, so no stage crate depends on another even in tests 3 (Pass 1) Pass 1
4. Pass 2 rules 1–7 over groups (with #1160's tumbling layouts); logical edges; metric rule; validity and pruning caps 3 (Pass 2), 4 logical crate
5. Stage 2 physical expansion (decision-tree levels 4–5), physical edges, pruned_candidates 2, 3 (Stage 2) Stage 2
6. Stage 3 baseline, realizer, export $x, y, z, t$; constraints (A)–(I); DagOfSelectedCandidates; the export and the backend change together. End-to-end from here 3 (Stage 3) plan-selection, executor, integration
7. Execution contexts $x^{\mathrm{mat}}v$, $x{\kappa,v}$, $y_{\kappa,u,c,i}$; costs $W^{\mathrm{mat}}v$, $c{\kappa,v}$, $\rho_\kappa$; $h_v$ and constraint (R) 5 plan-selection
8. Accuracy constraint (K): error bounds $E_c$, $D_c$ per candidate (per context), big-M on selected edges, root checks 4 plan-selection
9. Tools and snapshots stage_pipeline --text, devtools; snapshots re-baselined, every plan and cost change listed — all

Acceptance

  • Every notation has a home. Every row of the notation table names a type, field or function in the code.
  • Stage handoff. Each stage's boundary type carries the notation that stage defines.
  • Plans unchanged. Every corpus and sketch-plan snapshot selects a plan of the same cost, or the PR that changes it lists each difference.
  • Nothing old left. After the last step, none of the "Deletes" column exists in the code, and no PR added a fallback, flag or alias.

Related

Activity

  1. zzylol commented on Nov 5, 2026

    @zzylol
    ContributorAuthor

    New sub-work: #1217 (executor disk storage for Disk-medium materialized views and one-evaluation results). Stage 2 does not offer Disk until it lands; see #1216's state table.

  2. 100 remaining items

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions