Repository navigation
Conversation
zzylol
force-pushed
the
refactor/1207-stage3-baseline
branch
from
November 9, 2026 09:01
09d69f1 to
7d1bd55
Compare
zzylol
force-pushed
the
refactor/1207-contexts
branch
from
November 9, 2026 09:35
8a466a4 to
3b831f9
Compare
…}, h_v and constraint (R); asap-cost computes once per context (#1207 PR 7)
zzylol
force-pushed
the
refactor/1207-stage3-baseline
branch
from
November 9, 2026 12:11
7d1bd55 to
e53cf6e
Compare
zzylol
force-pushed
the
refactor/1207-contexts
branch
from
November 9, 2026 12:22
3b831f9 to
6e46d1f
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 #1236 (base
refactor/1207-stage3-baseline). Part of #1207 (PR 7 of the restack plan). One commit,6e46d1fd5(on PR 6e53cf6e09; previous head3b831f97fkept asbackup/1207-contexts-3b831f97f). Rebuilt on the restacked PR 6 (7d1bd5590); the previous head8a466a4c8is kept asbackup/1207-contexts-8a466a4c8(its realizer merge, inexact-objective terms andTumblingWindowChainGeometryhave no counterpart left after PR 6 and are not carried over).Why
PR 6's selection runs each candidate at most once, at the combined rate of every query reaching its group, and sets retention after solving. The formulation (concepts doc §5 "Execution contexts, by example", "Cost and retention") runs a candidate once per execution context that needs it, a materialized one once in its own context, and makes retention a decision:$h_v$ with constraint (R) and $\pi_v h_v$ in the objective.
What
AcrossEvaluations$v$ inMaterialized(v)), $y{\kappa,u,c,i}$ per edge in the consumer's context (only where the producerdelivers_onthat context),retention_msisdelivers_oninMaterialized(v)(wastodo!()), withPhysicalCandidate::chain_width_ms.Fixes (2026-11-09): Stage 3 edge costs went negative (an execution, its edge and its retention summed to −1,356,902/s on example 3a's shape): a candidate's baseline input was the most any candidate of the input group outputs, whatever its timing, so an ingestion-time chain was costed over the five years a materialized raw$\pi_v$ . The baseline is now over the producers the candidate may read (its admissible edges, $\pi_v$ × the least need ≥ 0). Concepts doc: "logical size of its inputs" says over which producers.
Scanretains for query-time consumers, which also inflated itsbaseline_inputs), and an ingestion-time consumer reads one batch of raw data, also from aScanmaterialized at ingestion time (reads_one_batch_of: left out of the baseline, and its edge costs what reading theScanthat is not materialized does). Pinned bycost::tests::no_execution_reading_a_producer_costs_less_than_nothing(per execution + edge +How
ExecutionContexts::newwalks from each query output: the query's context isEvaluation { grid }(evaluation_grid:evaluation_grid_msof its range evaluation at phase 0, 1 ms when unknown). For each consumer execution, its input context is its own, except: an ingestion-timeAcrossEvaluationsconsumer →IngestionPipeline(refresh window)(chain width, else its group's grid), so ingestion builds of one window share inputs; aTimeShift { at: End | Start }→ the grid of its queries' request end (start) times. Each admissible edge into the consumer whose producerdelivers_onthe input context becomesAcrossEvaluationsone inMaterialized(u).cost::execution_costs: logical outputs per group as in PR 6; each execution costed withnode_usageat its rateexecutions_per_second(ingestion 1; evaluation context:evaluation_rateof the context's queries reaching the group, the@-anchored subtree once per request;Materialized(m): 1000/w for a query-time chain, else once per request). Edge cost = consumer's cost in its context with inputScan) is costed at the most any of its consumers reads back;Scanread whole,@) has nomilp::model::solve_selectionadds (R):edge_retention_need_ms) and $h_v \ge \mathrm{span}q\cdot z{q,v}$;plan::plan_ofbuilds one node per selected execution in Kahn order.Key code interfaces added / modified
Permalinks at [
3b831f97f] (before the 2026-11-09 fix)(https://github.com/ProjectASAP/ASAPPlanner/tree/3b831f97f/crates).types/src/stages/physical/memo_candidates.rsstage3-plan-selection/src/milp/execution_contexts.rs(new, crate-private)stage3-plan-selection/src/cost.rsstage3-plan-selection/src/milp/model.rscost/src/execution_demand.rsUsage (planner test
runtime_install::one_chain_serves_two_evaluation_contexts, passing):Major algorithm changes
Selection over execution contexts. Problem: PR 6 ran a candidate once at the summed rate of every query reaching its group: two queries on one grid that share a not-materialized scan were costed twice, and one query every 10 min plus one every 30 min could not run a not-materialized candidate once per grid. Before:$x_v, y_{u,c,i}$ over candidates, rates per candidate, retention post-solve. After: How 1–4. Correctness: model tests — one-consumer rule per context (infeasible without $h$ = 300 s for consumers reading 1 m and 5 m (
OneEvaluationwhen both queries are on one grid), a None scan runs once per context on two grids (a_result_that_is_not_materialized_runs_once_per_context_that_reads_it), a materialized chain runs once anda_materialized_chain_runs_once_and_retains_what_its_consumers_read_back), (E) on a cycle; the planner test above end to end. Complexity: executions ≤ |C| × |contexts|; onenode_usageper candidate, per execution, per context edge and per retaining candidate (one more); MILP variables |executions| + |context edges| + Σ|G_q| + |retaining|. Limits: see Spec questions.Chain retention need (fix). Problem:
retention_need_msread one window back through any chain, so a chain rebuilt in each evaluation (ℓ = None) over raw data retained 10 min served a 60 min range (found by the new e2e test; with (R) that plan became the cheapest). After: only anAcrossEvaluationschain reads its newest window; any other passes through to what its consumers read. Test:a_chain_rebuilt_in_each_evaluation_reads_back_what_its_consumers_read(fails on the PR 6 rule: 10 s instead of 300 s).asap-cost contexts (K1). Before: a None node once per query reaching it; a query-time chain once per request over input rows / retained windows. After: once per evaluation grid; one window per width. Tests:
a_not_materialized_node_is_computed_once_per_evaluation_grid(two 10 s queries + one 30 s: multiplier 2, rate 1/10 + 1/30),a_query_time_chain_builds_one_window_per_width.Deleted
cost::SelectionCosts,cost::selection_costsExecutionCosts,execution_costsmodel::ExecutionContextVariables(#[allow(dead_code)]), thetodo!()retention_constraintsSelectionVariables; (R) implementedselect::evaluation_context,select::delivers_to_consumerevaluation_gridand the per-contextdelivers_onwalk inExecutionContexts::newplan_of's post-solve retention (output_span_ms,retention_need_msover the selected consumers, itsentriesparameter)retention_ms=delivers_on'stodo!()forMaterialized(v)Pending approval (new pub items not in the doc)
PhysicalCandidate::chain_width_ms(&self, &PhysicalASAPMemoCandidates) -> Option<u64>— shared bydelivers_on(own times ofMaterialized(v)) and Stage 3 (refresh window, ρ = 1000/w, Δ of π). Example: aSummaryTumblingWindowChain { tumbling_window_width_ms: 10_000, .. }candidate →Some(10_000); aSummaryAgg→None.ExecutionContexts { contexts, executions, edges }/ExecutionCosts { per_execution, retention_cost_per_second }:edgesholds(consumer execution, edge, producer execution)(the producer's execution depends on the consumer's input context, which differs from its own for ingestion builds and@subtrees), andExecutionCosts::per_edgeholdsTimeRange(1m)over chain v (IT, w = 10 s) → edge((κ10, range), v→range, (Materialized(v), v)); the chain's input edge((Materialized(v), v), scan→v, (IngestionPipeline({10 s, 0}), scan)).Spec questions (code kept independent where possible)
executions_per_second), which equals whatplan_costcharges when every such query uses the node. Recommend: doc says@ Thorizon. The workload has no horizon end, soneed = horizon end − T + rangecannot be computed; a producer read throughTimeShift { at: T }keeps PR 6's behaviour (noQueryWorkload { horizon_end: Option<TimestampMs>, .. }: withx @ 1700000000over[5m]andhorizon_end = 1700086400000, need = 86,400,000 + 300,000 ms; without it, per the decision, that candidate would not be offered.Scanread whole,@) has no@subtrees.computationsdoes not know a range query's@ end()subtree runs once per request; it charges it per step. Stage 3 charges once per request.Before this PR
One run per candidate at the summed rate of all reaching queries; retention set after solving from the selected consumers; a None chain over raw data could be planned retaining one window for a 60 min range.
After this PR
Each candidate runs once per context that needs it (once in total when materialized); retention is chosen by the MILP under (R) and costed in the objective; the plan carries one node per execution.
Verification
cargo testper crate (--all-features --no-fail-fast),cargo fmt --check,cargo clippy --workspace --all-features --all-targets -D warningsclean,crate_graphpasses. Wall-clock tests stay ignored.By test name vs PR 6 e53cf6e: 0 newly failing, 9 newly passing (below plus the cost pin). Vs the previous PR 7 head 3b831f9 (1301/74/9): newly passing the cost pin and PR 6's merge pin; newly failing
asap-plannerinstallable::tests::view_refreshes_compile_only_the_nodes_below_them, as in PR 6 (its old plan held raw rows at ingestion that the deployment does not retain; PR 5 now prunes thatScan).Earlier, vs PR 6 7d1bd55: 8 new tests pass (
delivers_on_the_own_times_of_a_materialized_computation; asap-costa_not_materialized_node_is_computed_once_per_evaluation_grid,a_query_time_chain_builds_one_window_per_width; Stage 3a_query_time_result_that_is_not_materialized_has_one_consumer_per_context(renamed from..._has_one_consumer),a_result_that_is_not_materialized_runs_once_per_context_that_reads_it,a_materialized_chain_runs_once_and_retains_what_its_consumers_read_back,a_chain_rebuilt_in_each_evaluation_reads_back_what_its_consumers_read; plannerruntime_install::one_chain_serves_two_evaluation_contexts). The 74 failures are PR 6's, with the same causes (devtools PR 9 todos; plan-shape/accuracy pins;fixed_intervalwithout phase, S4 — contexts do not change those, the queries have no known grid; executor gap 12).Self-review: no shims or aliases; one
retention_need_msfor costing and (R);evaluation_gridmoved, not copied; the per-candidate cost path, PR 6's post-solve retention and the#[allow(dead_code)]context variables are deleted. Over-abstraction check:ExecutionandExecutionContextsare the approved shapes; the triple inedgesandper_edgeare the only additions (Pending 2). Not done here: scan read cost on edges (P3).