Repository navigation
feat!: install the Planner's InstallablePlan; store its stored outputs as summaries - #838
Merged
Merged
Conversation
…s as summaries The control plane calls asap_planner::plan_installable once and writes the returned InstallablePlan as plan.json; the backend's own Stage 0-3 plumbing, pane-chain expansion and series-identity rewrite are gone. The SDS stores immutable summaries for every stored output the plan lists, built in an ingestion run or in a query run and kept: Sds::from_plan groups outputs by the SDS's own identity (sub-DAG hash, offset-only TimeShifts transparent), records which run builds each definition, keys summaries by (definition, coverage end), and keeps each definition for the largest retention_ms among its outputs (the Planner's retention, which includes range-evaluation spans) instead of "largest offset plus two panes". Each definition lists the stored outputs its builder reads, and Sds::ingestion_build_order orders ingestion-run definitions after their stored inputs. The precompute engine builds ingestion-run definitions in that order, from a RawSampleSelector over the plan's coverage window, and refuses one built from other stored outputs until it can build it once its inputs are sealed. Query-run definitions are left to the query engine; the backend still declares query_time_results_retained: false until that lands. Among Planner crates the workspace depends on asap-planner and asap-executor only, at ASAPPlanner main ecea897f; IR types come through asap_executor::planner. The sync script, its test and the CI README update both. asap-planner pulls in the SQL and MetricsQL frontends until they become optional features upstream.
An evaluation stored panes can serve is at most at the end of the newest closed pane, not at the watermark: with the watermark at 74:45 and Example 4b's 60-minute retention, the pane ending at 20 min is still read by the evaluation at 70 min, but was skipped as expired.
SummaryStore keys an instance by SummaryInstanceKey { definition,
selection }, with a time-only Selection ((end - width, end]) for now. This
is ASAPPlanner's summary coverage (definition plus selection) restricted
to time selections; when StoredOutput carries the coverage (ASAPPlanner
#781), the SDS takes the plan's definition id and selection instead of its
own hash and the coverage end.
…inputs - Panes are kept for one extra pane width beyond the plan's retention, as the design doc describes: with the watermark at 84:45 (newest closed pane 80 min), the evaluation at 70 min lost its pane ending at 20 min. The expired-pane skip uses the same bound. - Sds::ingestion_build_order refuses an ingestion-run definition that reads a query-run one, which is built only when an evaluation needs it. - PaneBinding.raw_input is removed: nothing here reads it.
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.
Base: main (#820 merged). Next: #821 (query engine), then query-run summaries, then the #814 watermark-barrier port.
Part of #837 (SDS stores summaries in general). This PR covers:
SummaryInstanceKey { definition, selection }, with the Planner's retention. This is ASAPPlanner's summary coverage (definition plus selection) restricted to time-only selections. OnceStoredOutputcarries the coverage (ASAPPlanner fix(control-plane): match a Planner fragment against its own subtree #781), the SDS uses the plan's definition id and selection instead of its own hash and coverage end;Still to come under #837: building and reusing query-run summaries (with the query engine, #821 and the kept-results PR after it), and summaries built from other stored outputs (after ASAPPlanner #781).
Why
The data plane worked out for itself what to store and for how long. It found the materialization frontier from node timings, walked each output's pane shape, and kept a definition's panes for "largest offset plus two pane widths". The Planner already returns all of this in its deployment handoff (
InstallablePlan): the outputs to store, how each one is built, the pane window it reads, and how long it must stay available, range-evaluation spans included. The design boundary says the Planner decides what to compute and keep, and the backend decides how.What
plan_workloadcallsasap_planner::plan_installableonce and returns itsInstallablePlan.save_plan/load_planwrite it as plan.json and read and validate it. The backend's Stage 0–3 plumbing, pane-chain expansion and series-identity rewrite are deleted, because the Planner does them. A non-PromQL workload is refused by the Planner's PromQL frontend.Sds::from_plan(&InstallablePlan)readsstored_outputs.SummaryDefinition, using the SDS's own identity (sub-DAG hash, offset-onlyTimeShifts transparent).RawSampleWindow).built_at). An output built in a query run and kept (StoredOutputBuild::QueryRun) is described like any other; the precompute engine leaves it to the query engine. Until the query engine builds such summaries, the backend still declaresquery_time_results_retained: false.stored_inputs).Sds::ingestion_build_orderorders ingestion-run definitions after their inputs and fails on a cycle. Today every stored output reads raw samples, so the lists are empty. The precompute engine refuses a definition with stored inputs until it can build one once its inputs are sealed (ASAPPlanner fix(control-plane): match a Planner fragment against its own subtree #781).ingestion_frontierand the pane-shape walk are deleted.retention_msamong its outputs instored_outputs. That is the Planner's retention per node: its input's offset plus its window plus the longest range-evaluation span of its readers. Retention is measured from the newest evaluation the stored panes can serve: the endEof the newest closed pane at or before the watermark. Panes are kept one pane width beyond the plan's retention, so an evaluation atE − pane widthis still served. Panes ending beforeoldest_kept_end_ms(E − pane width)are evicted, and when the watermark jumps they are skipped rather than built. This replaces "largest offset plus two pane widths". Example 4b without raw data keeps its 10-minute panes for the plan's 60 minutes plus one pane.ingestion_build_order. Each definition'sRawSampleSelectoris built from the plan's coverage window, and the backend's ownScansearch is gone.asap-plannerandasap-executoronly, at ASAPPlanner mainecea897f; IR types come throughasap_executor::planner.scripts/sync-asapplanner-main.sh, its test andscripts/ci/README.mdupdate both.asap-plannercurrently pulls in the SQL and MetricsQL frontends (DataFusion), so Cargo.lock grows by about 1,800 lines. ASAPPlanner docs: design offline ERP planning from physical selection to deployment #779 makes those frontends optional features; after it lands the backend can depend onasap-plannerwithdefault-features = false, features = ["promql"].How
plan_workloadbuilds aUserInput(PromQL frontend, built-in models with the backend's capabilities and plan selection settings) and blocks onplan_installable. PromQL planning completes on its first poll.save_planwrites the result as plan.json.Sds::from_plan): one pass overstored_outputs.TimeShifts transparent. The hash is the definition id.paneis the definition's window.retention_msand recordbuilt_at, with ingestion winning.stored_inputs), then topologically sort ingestion-run definitions (ingestion_build_order).TimeRangeand build aRawSampleSelectorfrom the window. Panes close as in feat(precompute): build stored summary panes from Remote Write samples #820.oldest_kept_end_ms(E − pane width), whereEis the end of the newest closed pane at or before the watermark. Panes are therefore kept one pane width beyond the plan's retention, so an evaluation one pane behindEis still served.Key code interfaces added / modified
Before this PR
plan.jsonwas the backend's ownInstalledPlan {queries, dag}. The data plane derived the frontier from node timings, walked each output down to itsScanto find its pane, and kept Example 4b's panes for 70 minutes (50-minute offset plus two 10-minute panes).After this PR
plan.jsonis the Planner'sInstallablePlan, and the data plane reads what to store and for how long from it. Example 4b without raw data has one definition of six 10-minute KLL panes, kept for the plan's 60 minutes.Usage
Over HTTP the same flow is
control_plane --workload … --deployment-capabilities caps.json --out plan.json(withcaps.jsoncontaining{"raw_data_retained": false}), thendata_plane --plan plan.json, then Remote Write./api/v1/status/asapreports the same counters.Verification
control_plane/tests/plan_workload.rs: round trip throughsave_plan/load_plan; Example 4b's six stored outputs (10-minute panes, offsets 0..50 min, largest retention 60 min); SQL refused by the Planner; a plan with a missing query refused byInstallablePlan::validate.data_plane/tests/sds.rs: shared definition and offsets, the largest retention among outputs, no definitions for a query-time plan, identity independent of node numbering, identity dependent on the source,QueryRunoutputs as query-run definitions, ingestion winning when outputs share a definition, build order and cycle detection,build_order_puts_inputs_first(three definitions),build_order_refuses_ingestion_reading_query_run, and the store.data_plane/tests/precompute.rs(19 tests): feat(precompute): build stored summary panes from Remote Write samples #820's tests with the plan's retention, plusdoes_not_build_query_run_summariesandkeeps_every_pane_the_newest_served_evaluation_reads, andkeeps_one_pane_of_margin_beyond_the_retention. The last one is the review repro (watermark 84:45, evaluation at 70 min): it failed on fae43fe and passes now, and it also checks that the store stays at 7 panes.keeps_every_pane_the_newest_served_evaluation_readsfailed on bc988c7: with the watermark at 74:45 the pane ending at 20 min was skipped, though the evaluation at 70 min reads it. The feat(query): answer planned instant queries by running the plan's roots #821 restack's HTTP test found this. A 30-day watermark jump builds the seven retained panes, and eviction at a 100-minute watermark keeps panes ending at 40..100 minutes.-D warningson Rust 1.99,cargo test --workspace, all--locked). All passed.