Repository navigation
feat(planner)!: Stage 2 materialization, the latency check and simplified query requirements (Example 4) - #717
Conversation
|
Can the display label etc DAG viewer related fields being separate from the physical candidate structures themselves? |
|
MaterializationUnit I also didn't understand what is this for |
Yes, and it should be. Planned change in this PR:
|
It is one summary build that Stage 2 can decide to store, plus its materialization options. It is now named Say Stage 1 chose KLL panes for q1 and exact max panes for q2. Stage 2 then finds two materialization candidates:
(A whole-window exact max would not get MatAtIngestion here: its 5m window is longer than the 1m interval.) One read by two or more queries also gets MatAtQuery (built once per evaluation, reused). Stage 2's physical candidates are the product of the options (here 2 × 2 = 4), less assignments that put a build at ingestion time over one that is not, and Stage 3 prices each one. It is only Stage 2's enumeration bookkeeping, so in this PR it is crate-private, with this example in its doc comment instead of being public API. The public result stays |
e0087ff to
2797e26
Compare
|
Renaming |
|
/// How one query is demanded, as planning reads it. Stage 2 reads recurrence and what's the difference between QueryDemand and Queryworkload/requirement? |
There's no real difference. Change in this PR:
|
9b09125 to
ece4f46
Compare
…(Example 4) Stage 2 returns one physical candidate per materialization assignment of each logical candidate's summary builds (NotMaterialized, MatAtIngestion when eligible, MatAtQuery when two or more queries read the build). Counter states over continuously ingesting PromQL data are eligible, on the assumption that such data arrives in time order per series. Stage 3 prices a maintained pane chain as the newest pane's update and N + 1 kept panes, charges a MatAtQuery state only for the one evaluation it is held, and rejects a candidate whose query latency is over its ExplicitMaxMs bound. Candidates hold planning data only (id, origin, roots, DAG, the materialization assignment); asap_devtools::candidate_labels derives the viewer labels. MaterializationUnit is crate-private Stage 2 bookkeeping.
…date One summary build Stage 2 may store, with its options; not a whole-workload plan like PhysicalCandidate. Related identifiers follow (materialization_candidates, candidates_below, mat_candidates).
…onal latency bound
QueryRequirements { accuracy: AccuracyTarget, max_latency_ms: Option<f64> }
replaces AccuracyRequirement { Explicit, ImplicitExact } and
LatencyRequirement { ExplicitMaxMs, Unspecified }. JSON: an omitted
accuracy is AccuracyTarget::exact(), an omitted max_latency_ms imposes no
bound, unknown fields are rejected, and the old tagged forms are not
accepted. The viewer document's query requirements use max_latency_ms.
…removed optimize_physical_asap, physical_candidate_count, select_plan, run_stage1_to_stage3 (and _from_pass1), run_stage2_and_stage3 and run_planning_stages_over take &[QueryWorkloadEntry], one per query in root order, and read the accuracy target, latency bound, recurrence and predictability from it. The separate targets and recurrence parameters and QueryDemand are removed. Every query now has a target (default exact), so Stage 3's internal per-query targets are no longer optional. QueryWorkloadEntry, Query and QueryRecurrence derive Default (empty query, exact, no latency bound, unknown predictability and recurrence).
- Un-ignore the four summary_sharing tests that now pass; the other three are ignored because plan selection still picks an exact plan there. - validate_ingestion_time_summary's doc: the planner checks filtered builds now. - price computes each query's reach once. - Document QueryLatency's fields and CandidateCost::latency.
#716's rollup test marked shared nodes MatAtQuery by hand as a stand-in for G8; Stage 2 now offers it for the shared sums, and plan selection picks the rollup unaided. The exact top-k test keeps the stand-in: Stage 2 builds a shared summary once per evaluation, not a shared exact ranking.
fb74f70 to
0723f05
Compare
Supersedes #604 and #606. Group G8 of the ASAPPlanner consolidation (#509 Example 4, Stage 2 materialization). Also simplifies the per-query requirements (
QueryRequirements) and passes the workload entries straight into planning.Why
QueryRequirementswrapped its two fields in enums:AccuracyRequirement { Explicit(target), ImplicitExact }andLatencyRequirement { ExplicitMaxMs(ms), Unspecified }. Both say "a value or the default", whichAccuracyTarget(default exact) andOption<f64>already say. Planning then took the accuracy targets and the recurrences as two separate lists beside the roots.What
Stage 2 enumerates materialization. Each summary build (
SummaryAggorPaneChain) is one materialization candidate (MaterializationCandidate), with up to three options:NotMaterialized;MatAtIngestion(Memory), when the build is eligible (S4);MatAtQuery(Memory), when two or more queries read it. The state is reused within one evaluation only; keeping it across evaluations (B3) and the deployment capability gate are G9.optimize_physical_asapreturns aVec: one physical candidate per assignment, the all-NotMaterializedone first. An assignment that puts a build at ingestion time over a build that is not is not generated (docs: propose workload-wide planning, summary sharing, and materialization #509 Algorithm 3, line 13). The enumeration is uncapped. Ids areP<n>, thenP<n>-m<k>. Each candidate carries its assignment (materialization: each summary build's option, by DAG node id).Candidates hold planning data only.
PhysicalCandidate,LogicalCandidate,Stage1CandidateandPrunedCandidateno longer havedisplay_name. The viewer labels (e.g. "Q1 exact · Q2 CMS+heap · ingestion time: Kll ×5 panes") are pure functions inasap_devtools::candidate_labels, written into thestage_pipelinedocument only.Ingestion-time eligibility (S4). A build may run at ingestion time only when all of these hold:
Predictable;FixedIntervalAt(a known phase), over an unshifted window no longer than the interval;Each refusal is an
IngestionPruneReason. That type andMaterializationCandidateare crate-private Stage 2 bookkeeping. On the planner side, the timing pass now rejects a filtered build placed at ingestion time (FilteredSummaryAtIngestion), which matches the executor's check.Cross-query phase split.
split_shared_by_phasenow takes every root of the workload. A node that one query reads at ingestion time and another at query time is copied, one copy per phase.MatAtQuery memory. A
MatAtQuerystate is reused only within one evaluation, so it is charged only for the time it is held: its bytes × the latency of the longest query reading it, once per evaluation (close to nothing). It is not charged retained byte-seconds over the interval. Keeping state across evaluations (B3) is G9 and will be charged by its real retention.Pane chains at ingestion time. The planner prices a chain as a chain: only the newest pane pays the update, over one second of arriving rows, and the chain keeps
N + 1panes (bytes × seconds).expand_pane_chainsnow carries the chain's timing and materialization onto its panes. Before this PR it dropped them.Latency check (S6). This follows sketch-bench
docs/rqe_optimizer_cost_model.md§5. A query's latency is its longest chain: the compactionc_Dof the newest window of an ingestion-time state it reads, plus its own query-time workℓ, each on a full core, with elastic CPU (no queueing). A candidate with any query over itsmax_latency_msis invalid, and the reason gives the latency. The milliseconds come from two newResourceCalibrationfields,ms_per_cpu_opandms_per_scan_byte(1 ns and 0.1 ns in the illustrative calibration). G9 makes them deployment-suppliable.CandidateCost::latencyreports every query's latency.QueryRequirements { accuracy: AccuracyTarget, max_latency_ms: Option<f64> }.AccuracyRequirementandLatencyRequirementare removed. JSON: an omittedaccuracyisAccuracyTarget::exact(), an omittedmax_latency_msimposes no bound, unknown fields are rejected, and the old tagged forms ("implicit_exact",{"explicit": …},response_latency) are not accepted. There is no compatibility path.Planning takes the workload entries.
optimize_physical_asap,physical_candidate_count,select_plan,run_stage1_to_stage3(and_from_pass1),run_stage2_and_stage3andrun_planning_stages_overtake&[QueryWorkloadEntry], one per query in root order. They read the accuracy target, latency bound, recurrence and predictability from it. The separatetargets/recurrenceparameters are removed. Every query now has a target (default exact), so "a query without a target" no longer exists.QueryWorkloadEntry,QueryandQueryRecurrencederiveDefault.Devtool. The
stage_pipeline10^4 limit now also counts Stage 2's physical candidates, before it builds them. Its document's per-querylatency_msis nowmax_latency_ms.Docs. The PR changes four Markdown files:
docs/user_guide_docs/stage-viewer.md(the Example 1 and 3b rows),docs/design_docs/proposals/planner-layering-example4-acceptance.md(test statuses and the resolved open question),docs/design_docs/architecture/input-output-workflow.mdanddocs/develop_docs/library-api.md(the newQueryRequirements).How
stage2_physical_asap_optimization.rs:materialization_candidatesfinds the builds and their readers, then checks the S4 rules against the readers' entries. As a final check, it applies timings with only the build and the builds below it at ingestion time.materialization_assignmentswalks the mixed-radix product of the options and skips assignments that are not down-closed.stage3_plan_selection.rs:select_plantakes each entry's accuracy target, recurrence andmax_latency_ms. The internal per-query targets are no longerOption.pricerecords each node's work in milliseconds. That is one computation at query time, or one second of arriving rows at ingestion time.query_latenciessums the query-time work each query reaches, counting each node once. It adds the largest compaction among the ingestion-time states the query reads:(k − 1) · I · ms_per_cpu_op, wherek = ⌈ingest ms per s / 1000⌉, at least 1, andIis the number of states per window.MatAtIngestionchain keepsstate / N · (N + 1)and a whole window2 × state, every second. AMatAtQuerynode records(state, evaluations per second)while priced. Once the latencies are known,priceaddscost_per_retained_byte_second × state × (longest reader's latency) × evaluations per second. Each query's reach (its walk, stopping at ingestion-time nodes) is computed once.select_planrejects a candidate whose latency is over a bound before recording its cost.pass/stage1_to_stage3.rs: the driver passes the entries to Stage 2 and Stage 3, and the Stage 1 recurrences come from them.devtools/candidate_labels.rs:candidate_labels(&PlanningStagesRun)derives eachL<n>label from the Pass 1 choice (choice_label) and the Pass 2 rules applied (pass2_label, including feat(planner): Pass 2 reuse rules (rollup, exact top-k reuse) and nested sum merge in Pass 1 (#680) #716's reuse rules). Each physical label is its logical label plus the non-NotMaterializedentries of its assignment (physical_label).PlanningStagesRunkeeps the Pass 1 inventory and the Pass 2 records they read.Key code interfaces added / modified
crates/types/src/workload.rscrates/asap-aware-mapping/src/stage2_physical_asap_optimization.rscrates/asap-aware-mapping/src/stage3_plan_selection.rs,analytical_cost.rscrates/asap-aware-mapping/src/pass/stage1_to_stage3.rscrates/types/src/ir/timing.rs,post_asap/execution_data_state.rs,ir/pane_chain.rscrates/devtools/src/candidate_labels.rs(new),planning_stages.rsThe
asap-stage-pipeline/v1document keeps its shape. Each candidate'sdisplay_namecomes fromcandidate_labels, there are more physical candidates (P<n>-m<k>), and each query'srequirements.latency_msis nowrequirements.max_latency_ms.Usage
End to end, as a test runs it on the head (#509 Example 4, Pattern B). The query's workload entry, with
max_latency_ms, goes through Stage 2 materialization and Stage 3, and ends at the selected plan, its materialization and cost:Before this PR
expand_pane_chainsdropped a chain's materialization.split_shared_by_phaseworked on one root only.AccuracyRequirement/LatencyRequirementenums, and planning took separate accuracy-target and recurrence lists.After this PR (built-in models, illustrative constants)
MatAtQueryfor builds two queries share, and panes at ingestion time. 164 invalid (accuracy)Example 4 tests:
retention_msnow derives the kept span (B1: 6 min ≥ 5 min)Verification
types: a filtered build never runs at ingestion time; a cross-root split copies only the scan two queries read at two phases; expansion carries the chain's timing.rateandincreasepanes over ingesting PromQL data run at ingestion time, while a counter over a table source or mixed data stays at query time;MatAtQueryfor a build read by two queries; the scan split across queries. These two counter tests and the MatAtQuery memory test were written failing first.MatAtQuerystate costs state × evaluation latency × evaluations per second; an ingestion chain pays one second of rows × (depth + 1) and keeps 6 × series × state bytes; the latency bound rejects B2 with its latency and selects B1; compaction appears once ingestion needs more than one core.tumbling_pane_merge.rstakes the planner's pane chain assignedMatAtIngestion, expands it (the panes carryMatAtIngestion), builds the panes inScope::Ingestionruns and merges them at query time. The result is identical to the single build for sum, p99, rate and increase.query_requirements_json_is_a_target_and_an_optional_bound(written failing first):{}parses to exact with no bound,max_latency_msround-trips, and the old tagged forms and unknown fields are rejected.QueryWorkloadEntryvalues (test_support::entry/entry_withinasap-aware-mapping,..Default::default()elsewhere) or readworkload.query_workload.entries().count_min_is_admitted_without_a_targetis deleted: every query now has a target, and under any target a Count-Min over a metric not declared a counter is rejected.summary_sharing: four tests that now pass are un-ignored (quantiles_with_equal_params_share_one_producer,cross_series_p50_and_p99_share_one_producer,identical_ungrouped_queries_share_their_producers,identical_sql_percentiles_share_one_producer). The other three stay ignored because plan selection still picks an exact plan for one of their queries.reuse_rules(feat(planner): Pass 2 reuse rules (rollup, exact top-k reuse) and nested sum merge in Pass 1 (#680) #716): the rollup test now reads Stage 2's ownMatAtQueryinstead of the hand-written stand-in, and plan selection picks the rollup unaided. The exact top-k test keeps the stand-in, because Stage 2 builds a shared summary once per evaluation, but not a shared exact ranking.operator_design_examples::batch_planning_replaces_summary_operatorsnow selects oneMatAtQuerySUM state for both queries.asap_devtools::candidate_labels(Example 1: choice, Pass 2 and ingestion-time labels; Example 2: "query time, shared: UnivMon"). Stage 2 tests assert the candidate'smaterializationinstead.exact_quantile_state_costs_more_than_a_sketch_over_large_groupskeeps the state at ingestion time, sinceMatAtQuerystate is no longer kept across evaluations.cde2ca3d(feat(planner): Pass 2 reuse rules (rollup, exact top-k reuse) and nested sum merge in Pass 1 (#680) #716):cargo nextest run --workspacepasses: 1856 tests, plus the two slow Example 4 tests run separately.cargo fmt --all -- --checkandcargo clippy --workspace --all-targets --all-features --locked -- -D warningspass.cargo test --doc -p asap-typespasses, and so doestools/verify_metricsql_external_consumer.sh.tools/dag-viewer:python3 -m unittest test_server test_viewerpasses (50 tests).Limitations and follow-ups
ms_per_cpu_op/ms_per_scan_byte, and Example 4's model-relative B1/B2 tests (Q49).MaintainPopulationstays as before.stage-viewer.mddoes not yet mention theMatAtQueryplans. Docs are tracked in Update the docs after the G1–G11 consolidation groups land #677.IngestionPruneReason) is now internal. If it should reach the user, it would go through the candidate or report, not theMaterializationCandidatetype.