Repository navigation
refactor(stage2): physical expansion (decision-tree levels 3–4), physical edges, pruned alternatives; Count-Min weights via column_sign (#1207 PR 5) - #1235
Draft
zzylol wants to merge 1 commit into
Conversation
zzylol
force-pushed
the
refactor/1207-pass2
branch
2 times, most recently
from
November 9, 2026 07:11
bdf0888 to
bcae9e5
Compare
…natives; Count-Min weights via column_sign (#1207 PR 5) Stage 2 expands each logical candidate Pass 2 kept into its offered (timing, lifetime): ingestion time for time-independent groups and their inputs (counter states only over ordered rows), AcrossEvaluations for chains and grid-evaluated groups. Prune reasons: NoIngestionPipeline, RawDataNotRetained, QueryTimeResultsNotRetained, CountMinWeightsNotProvenNonNegative, InputHasNoBuildableProducer, ReachesNoQueryOutput. Physical edges carry alpha^log and alpha^phys; PhysicalASAPMemoCandidates indexes the admissible ones. Deletes WeightDomain, NonNegativeWeightProof and SummaryUpdate::weight_domain (the state group's column_sign replaces them), Pass 1's copy of reads_only_time_series, and per-crate memo test fixtures (asap_types::test_support).
zzylol
force-pushed
the
refactor/1207-stage2
branch
from
November 9, 2026 07:43
d11f12e to
616f765
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #1234 (
refactor/1207-pass2, PR 4). Part of the #1207 merge unit: do not merge on its own.Why
#1207's Order, row "5. Stage 2": expand each logical candidate Pass 2 kept into its physical alternatives$p^{\mathrm{phys}} = (\tau, \ell)$ , each logical edge into physical edges with $\alpha^{\log}$ and $\alpha^{\mathrm{phys}}$ , and record every rejected alternative with its
PruneReason. Concepts doc §2 "Decision tree" (levels 3–4), "Physical requirements", and §3 "Stage 2: physical alternatives" (data arrival, prune reasons). Decisions applied: F1–F3 and D1–D13 of the PR 5 review (2026-11-07), G2-counter and G4/H3 (no executor-support check anywhere), L8a (Count-Min prune viacolumn_sign), time indexing (any grid-evaluated group may beAcrossEvaluations), and "WeightDomaindeleted in PR 5" (2026-11-09).What
generate_physical_asap_memo_candidates(logical, data, capabilities)(wastodo!()):catalog/configurations.rsenumerate_candidate_configurationsGroupProperties::depends_on_evaluation_timefalse) or an input of one (ingestion_time_offered_per_group), and for a counter state (rate/increase/irate) only when its rows are ordered (max_out_of_order_msdeclared, or PromQL rows); ℓ = None, OneEvaluation(Memory), and AcrossEvaluations(Memory) only for a chain or a group with a knownevaluation_grid_of_group. Values not offered are not recordedCountMinWeightsNotProvenNonNegative(aCms/CmsWithHeap/HydraCmsbuild whose state column'scolumn_signis notNonNegative),NoIngestionPipeline(IT, arrivalAtRest/Unknown),RawDataNotRetained(QTScan),QueryTimeResultsNotRetained(QT AcrossEvaluations)catalog/mod.rsproducer_satisfies_physical_requirements(producer, consumer_timing)catalog/pruning.rsInputHasNoBuildableProducer(least fixed point of "every input has a buildable admissible producer"),ReachesNoQueryOutput(not reached from a query output, an alternative of a root group a query-time consumer may read)PhysicalASAPMemoCandidates::newadmissible_edges_into,admissible_edges_out_of)Candidates Pass 2 eliminated (
LogicalASAPMemoCandidates::pruned) are not expanded. Stage 2 checks no executor support and costs nothing; alignment with each execution context stays Stage 3's (PhysicalCandidate::delivers_on, implemented in PR 1).Deleted (replaced, no shims)
SummaryUpdate::weight_domain,WeightDomain(UnknownOrSigned,NonNegative,RawDataSamplesOfMetric,CounterDerivativeOfMetric),NonNegativeWeightProof(5 variants)column_sign(derived from the weights' sign and the declared source signs) read by Stage 2'sCountMinWeightsNotProvenNonNegative(decision 2026-11-09)CmsWithHeapgate on unproven weights inrealize_keyed_additive_summary_input,raw_data_samples_of_metric,with_raw_data_samples_requirement,SingleMeasure::raw_data_samples_ofreads_only_time_series(memo, input)asap_types::stages::reads_only_time_series(memo, group), which calls the one generic copy inir::schema::topk(D12)CmsWithHeap"nonnegative weight contract" check (physical_planner), the rate-weight refusal increate_planner_accumulator(keyed offResetAwareCounterDerivative), the Hydra "weight's sign is unknown" binding check; testsweighted_topk_binding::rate_updates_cannot_enter_integer_heap_factory,hydra_cms_execution::unknown_sign_weight_is_rejectedinvalid_weight_values_fail_the_build)try_candidate,candidate,group,series_schema,scan_operator; Pass 1'sscan_operatorasap_types::test_support::{add_candidate, group_with_own_candidate, series_schema, time_series_scan}(D11)thiserrordependency ofasap-stage2-physical-optimizationNot reintroduced from the old PR 5 head (decisions after it):
executor_can_run/PerEntityAggregateNotExecutable(G4/H3: no executor-support check),AcrossEvaluationsWithoutWindowLayout(F1/C7: a value-set rule, not a prune reason),PhysicalCandidateKey,PrunedCandidate.group,PhysicalASAPMemoCandidates::query_roots, the privateDeploymentstruct (D1–D4, F2, F3; the PR 0 types already have these shapes).Pending approval (new pub items not in the concepts doc)
N1.
PhysicalEdge::is_admissible(review D6, approved; not in the doc's interface listing):Example: in query A below, the edge from the scan's (QT, None) alternative into the chain's (IT, AcrossEvaluations) has
satisfies_logical_requirement: true, satisfies_physical_requirement: false(an ingestion-time build cannot read a query-time scan), sois_admissible()is false and neither Stage 2's pruning noradmissible_edges_intolists it.N2.
asap_types::stages::reads_only_time_series(review D12, approved; not in the doc):Example: for
rate(c_total[5m])'s chain overScan(TimeSeries c_total)it is true, so the counter chain is offered ingestion time withmax_out_of_order_ms: None; overScan(Table events)it is false, and the counter chain is offered ingestion time only withmax_out_of_order_ms: Some(_). Pass 1 uses it for the PromQL top-k rule.N3. Shared memo fixtures under the
test-supportfeature (review D11, approved; test-only):Example:
group_with_own_candidate(&mut memo, time_series_scan("latency", series_schema(&["job"]), vec![]))is query A's scan group; Pass 2's and Stage 2's tests build their memos with these.Design choices taken (conservative; please confirm)
C1.
QueryTimeResultsNotRetainedprunes only (QueryTime, AcrossEvaluations). The concepts doc's prune-reason table (and PR 0's comment on the variant) says OneEvaluation too; the doc's decision-tree diagram,DeploymentCapabilities::query_time_results_retained("materialized across evaluations"; default false) and the old PR 5 say AcrossEvaluations only. Taken: AcrossEvaluations only, because with the default capabilities pruning OneEvaluation would remove every within-evaluation sharing (decision C3: aNoneproducer has at most one consumer per context). Example: query A with default capabilities keeps the readout's (QT, OneEvaluation) and prunes its (QT, AcrossEvaluations). If approved, the doc's table row reads "QueryTime with ℓ = AcrossEvaluations".C2. Ingestion time is offered through inputs transitively. The doc says "a candidate that is time-independent or an input of one". Taken: the closure: an input of a candidate of an offered group is offered too (an ingestion-time candidate reads only ingestion-time producers, so a one-level rule could not build anything below an
@ T). Example:sum_over_time(m[1m] @ 1000):TimeShift @1000is time-independent; its inputTimeRange 1mand that one'sScanare offered (IT, None) (testan_at_t_subtree_is_offered_ingestion_time). Without@ Tthe closure adds nothing: inputs of time-independent groups are time-independent.C3. Count-Min weight sign read from the state group. The doc says "whose weight column's
column_sign". Taken: the sign the build's own group records for its state column (Stage 0 derives it from the weight: a constant's sign, or the weight column'scolumn_signin the input group), so no second derivation of the weight's sign. Equal for every candidate of the group, since they produce the same state. Example:CmsWithHeapkeyed by series, weighted bySampleValueofm: pruned in all three alternatives (QT None,QT One,IT None) without a declaration, offered withcolumn_signs: {"value": "non_negative"}form(testa_count_min_build_needs_weights_proven_non_negative). It is checked first: it holds for every (τ, ℓ).C4. A query reads its output as a query-time consumer (review G3, doc line pending). A query output is an alternative of a root group that
producer_satisfies_physical_requirements(_, QueryTime)admits: any query-time alternative and (IngestionTime, AcrossEvaluations); (IngestionTime, None) is not, since its result is gone when the query runs. Example:sum_over_time(m[1m] @ 1000)'s rootTimeShiftkeeps (QT, None) and (QT, OneEvaluation); its (IT, None) isReachesNoQueryOutput.C5. The executor no longer refuses rate-weighted updates in
create_planner_accumulator. That refusal readNonNegativeWeightProof::ResetAwareCounterDerivative; with the proof gone, the factory cannot tell a rate weight from a sum weight. Listed for PR 6 (typed binding of window-weighted heaps).How
PhysicalASAPMemoCandidates::newbuilds the admissible-edge indexes (HashMap<(consumer, input), Vec<PhysicalEdgeId>>,Vec<Vec<PhysicalEdgeId>>by producer) throughPhysicalEdge::is_admissible; the two accessors read them (weretodo!()).Key code interfaces added / modified
crates/stage2-physical-optimization/src/catalog/mod.rscrates/types/src/stages/physical/memo_candidates.rs,stages/memo.rs,stages/pruning.rscrates/types/src/ir/schema/sketch.rsUsage (end to end; test
offers_each_candidate_its_timings_and_lifetimesat 616f765, query A of the concepts doc):Major algorithm changes
Stage 2 expansion and pruning (new; the previous head's version was written against the old memo and is replaced).
todo!().TIMINGS × LIFETIMESfiltered by the offered values, each with the first matching prune rule; (3) per Pass 2 edge chunk (c, i), the product of c's and the producers' alternatives; (4) buildable = least fixed point, reached = DFS from query outputs over admissible edges into buildable producers; (5) renumber, sort edges, index.generatehelper, which also checks the edge order and the indexes); a pruned alternative is recorded exactly once, with the first reason that applies.Before / After
Before (PR 4 head): Pass 2 produced
LogicalASAPMemoCandidates; Stage 2 wastodo!(), so every planning run stopped there. Count-Min weight signs were aWeightDomainproof on each update, set by Pass 1 and checked by the executor.After: Stage 2 produces
PhysicalASAPMemoCandidateswith physical edges, indexes and the pruned list; planning stops at Stage 3 (select_plan'stodo!()of PR 6). A Count-Min build's weight sign is the state group'scolumn_sign, and Stage 2 records an unproven one asCountMinWeightsNotProvenNonNegative.Representation, rules, enumeration
PhysicalCandidate { logical_candidate_id, physical_properties };PhysicalEdge { …, satisfies_logical_requirement, satisfies_physical_requirement };PrunedCandidate<PhysicalCandidate> { candidate, reason }.Self-review
is_admissible).reads_only_time_series, one set of memo test fixtures, one weight-sign derivation (the state column'scolumn_sign;WeightDomaingone).thiserrordropped from Stage 2.planner-layering.mdStage 2 rows and pruning paragraphs (D13.1),asap-primitive-schema.mdandglossary.mdwithoutWeightDomain; the executor's filtered-ingestion comment no longer claims the planner never offers one. The concepts doc is not changed (C1 needs a decision first; D13.2's crate path is alreadystage2-physical-optimizationthere).Verification
cargo fmt --all,cargo clippy --workspace --all-features --all-targets -- -D warnings: clean;crate_graphpasses.@ Tclosure, counter order (SQL vs PromQL, bound declared or not), Count-Min sign, edge requirements, an input without an admissible producer, Pass 2's eliminated candidates, a scalar root's operand).Test status at 616f765 (
cargo test -p <crate> --all-features, per crate)avgfrom Pass 1's memo: 3; OTHER: calledResult::unwrap()on anErrvalue: Error("invalid type: sequence, expected string or map", line: 0, column: 0): 1PR 4 (bcae9e5): 1155 / 188 / 9. By test name: no test that passes on PR 4 fails here. 11 new Stage 2 tests pass. Two executor tests are deleted with the checks they tested (
weighted_topk_binding::rate_updates_cannot_enter_integer_heap_factory, passing;hydra_cms_execution::unknown_sign_weight_is_rejected, already failing at PR 6's θtodo!()). Every planning failure that stopped at PR 5's "physical expansion and physical edges" (planner 18, devtools 41, integration 43, executor 1) now stops at PR 6'sselect_plan("the baseline MILP over x, y, z, t, …"); 2 devtools tests now stop at PR 9's pruned-candidate printout.