Repository navigation
refactor!: plan with ASAPPlanner main's stage pipeline and drop the old execution stack - #818
Merged
Merged
Conversation
This was referenced Oct 7, 2026
…ecution stack The backend now executes ASAPPlanner's selected PhysicalASAPDAG as given (#509): the Planner runs Stages 0-3, the backend supplies deployment inputs. - Pin every Planner crate the backend uses to ASAPPlanner main (58999df2): asap-types, asap-frontend-promql, asap-logical-asap-optimization, asap-plan-selection, asap-executor. The sync script updates all five. - control_plane: lower a PromQL PlanningWorkload, run Stages 1-3 (run_stage1_to_stage3) under DeploymentInputs (DeploymentCapabilities and PlanSelectionSettings), and write the selected candidate's DAG, with each PaneChain expanded into its panes, plus the query each root answers (plan.json). - data_plane: load and validate plan.json, accept Remote Write, forward queries to Prometheus until the query engine lands. - Remove the backend's own plan selection, placement pricing, ERP, SDS, precompute and query engines, ClickHouse, OTel and OpAMP paths, which were built on the removed Planner APIs.
zzylol
force-pushed
the
feat/physical-dag-handoff
branch
from
October 18, 2026 15:40
77f23de to
440851b
Compare
zzylol
marked this pull request as ready for review
October 19, 2026 23:43
cargo update -p asap-executor exits 101 because no workspace member depends on the crate, so it is absent from Cargo.lock. The sync test's fake cargo now rejects -p specs missing from the real Cargo.lock and runs the script against the repository manifest.
…y headers - Raise the write route's body limit from axum's 2 MB default to Limits::max_compressed_bytes; larger bodies get 413. - Forward end-to-end request headers (Authorization, X-Scope-OrgID, ...), dropping hop-by-hop headers, Host, Content-Length and Accept-Encoding. - Bound forwarded queries with --forward-timeout-seconds (default 120). - State in the README and --prometheus-url help that Remote Write samples are counted and dropped until the precompute engine lands.
The rewrite failure was ignored and the root planned without the row identity per-series state needs at runtime. Return it as PlanError::SeriesIdentity, as the Planner's executor lowering does.
- Delete .dockerignore: no Dockerfile remains. - Drop the hadolint hook and .gitignore entries for removed outputs; ignore __pycache__ from the updater tests. - scripts/ci/README.md: describe what MVP CI runs now and drop the outdated PR reference.
This was referenced Oct 20, 2026
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.
Stack 1/5 for executing the plan ASAPPlanner selects (#509's physical ASAP DAG). Next: #819.
Why
The backend selected its own plans, with its own ERP, SDS, precompute and query engines, and each part pinned a different Planner revision, so none of it ran the plan the Planner selects. The Planner now selects plans end to end on its
main; the backend should only supply deployment inputs and execute the result.What
PlanningWorkloadwith the Planner's Stages 0–3 under the backend's deployment inputs and writesplan.json(InstalledPlan): the selectedPhysicalASAPDAGplus the query each root answers. A root whose rows cannot carry the full series identity is rejected (PlanError::SeriesIdentity), not planned without it.Limits::max_compressed_bytes(32 MiB), and forwards every query to Prometheus with its end-to-end headers (Authorization,X-Scope-OrgID, ...) under a timeout (--forward-timeout-seconds, default 120). Until the precompute engine lands (feat(precompute): build stored summary panes from Remote Write samples #820), Remote Write samples are validated, counted and dropped: Prometheus must receive Remote Write itself. Later PRs in the stack answer planned queries from the plan.main58999df2 for the four crates the backend uses:asap-types(asplanner-types),asap-frontend-promql,asap-logical-asap-optimization,asap-plan-selection.scripts/sync-asapplanner-main.shand its test update these four pins.asap-executoris not pinned here because no crate uses it yet (andcargo update -pfails for a package absent fromCargo.lock); the first stack PR that uses it adds the pin and its-pline back.examples/planner-layering-example1.json, test(promql): enforce range step parity in production E2E #509 Example 1 in the current workload format (DataWorkload { arrival, sources },AccuracyTarget,QueryRequirements { accuracy, max_latency_ms }), generated from the Planner's own example definitions.crates/,docs/,demos/,tools/, the Python package,.dockerignoreand the hadolint hook. The table in Deleted capabilities lists each piece and its replacement; the ones not replaced yet are tracked in Capabilities removed with the old backend that are not replaced yet (#818) #823.Supersedes #788–#812, #791 and #779.
Deleted capabilities
Every piece of the old backend this PR deletes, and what replaces it. "Deferred" rows have no replacement yet and are tracked in #823. Every path is checked against
git diff --name-status main...HEAD, and every deleted file falls under some row.control_plane/src/planner_selection.rs,control_plane/src/physical/workload_cost.rs,control_plane/src/physical/workload_cost/status.rs,control_plane/src/physical/post_asap/cost_model.rs,control_plane/src/physical/deployment_cost/mod.rs,control_plane/src/physical/deployment_cost/tco.rs,control_plane/examples/workload_cost_manifest.rsselect_plan, cost models 1–3)control_plane/src/physical/erp.rs,tools/empirical-bench,control_plane/examples/calibration_candidates.rs,control_plane/examples/offline_frequency_plan.rs,control_plane/tests/offline_evidence.rs,control_plane/tests/fixtures/erp-kll-measured.jsonmeasured_operation_costs, imported from sketch-benchcontrol_plane/src/accuracy.rs,control_plane/src/epsilon_alloc.rsAccuracyModelcontrol_plane/src/physical/workload_cost/materialization_candidates.rs,control_plane/src/physical/pane_reuse.rs,control_plane/src/physical/maintained_population.rs,control_plane/src/physical/compiler/windows.rscontrol_plane/src/physical/sketch_catalog.rs,control_plane/src/physical/summary_catalog.rs,control_plane/src/physical/runtime_capability.rs,control_plane/sketch_capabilities.yml,control_plane/sketch_params_default.ymlDeploymentCapabilitiescontrol_plane/src/query_parser,control_plane/src/physical/compiler.rs,control_plane/src/physical/post_asap/lower.rs,control_plane/src/physical/post_asap/matcher.rs,control_plane/src/physical/post_asap/deployment_expr.rs,control_plane/src/physical/post_asap/mod.rs,control_plane/src/physical/post_asap/tests.rs,control_plane/src/physical/realization.rs,control_plane/src/physical/executable_binding.rs,control_plane/src/physical/backend_stage.rs,control_plane/src/physical/mod.rs,control_plane/src/query_plan.rs,control_plane/src/query_plan/query_time.rs,control_plane/examples/compile_workload_artifact.rsplan.jsoncontrol_plane/src/physical/deployment_cost/wire.rs,control_plane/src/emit/backend_wire.rs,control_plane/src/emit/mod.rs,control_plane/src/backend_client.rs,control_plane/src/physical/publication.rs,control_plane/src/physical/plan_dot.rs,control_plane/src/types.rs,control_plane/src/workload.rs,control_plane/proto/anyvalue.proto,control_plane/proto/control_plane.proto,control_plane/proto/feedback.proto,data_plane/src/storage_engines/types/installed_precompute_plan.rs,data_plane/src/storage_engines/types/storage_backend.rs,data_plane/src/storage_engines/types/enums.rs,data_plane/src/storage_engines/types/mod.rsplan.json(InstalledPlan), loaded by the data planedata_plane/src/storage_engines/types/hot_reload_config.rsArcSwap)control_plane/src/clickhouse.rs,control_plane/src/query_plan/clickhouse_exact.rs,control_plane/examples/compile_clickhouse_workload.rs,control_plane/examples/audit_clickhouse_corpus.rs,control_plane/examples/support/automatic_clickhouse_corpus.rs,control_plane/tests/fixtures/sql_exact_cuts,data_plane/examples/audit_clickhouse_fallback.rscontrol_plane/src/opamp,control_plane/proto/opamp.proto,control_plane/src/emit/monitor.rs,control_plane/src/physical/deployment_cost/online.rs,control_plane/src/runtime_samples.rs,control_plane/src/metrics_exposer.rs,data_plane/src/bincontrol_plane/examples/offline_planner_replay.rs,tools/run-offline-planner-replay.py,tools/o11y-execution,tools/shared-workload,tools/test_shared_panes.pycontrol_plane --workload ... --out plan.jsonplans offline; the correctness e2e test (#822) and the planned benefit harnesscontrol_plane/build.rsCargo.toml/Cargo.lockcrates/asap_typesasap-types, pinnedcrates/asap_otel_proto,data_plane/src/drivers/ingest/otel.rsdata_plane/src/drivers/ingest/prometheus_remote_write.rs,data_plane/src/drivers/ingest/series_resolver.rs,data_plane/src/drivers/ingest/mod.rs,data_plane/src/drivers/mod.rsdata_plane/src/remote_write.rs; samples counted and dropped); samples selected by the plan'sScan(#820)data_plane/src/drivers/query/adapters/prometheus_http.rs,data_plane/src/drivers/query/adapters/config.rs,data_plane/src/drivers/query/adapters/traits.rs,data_plane/src/drivers/query/adapters/mod.rs,data_plane/src/drivers/query/fallback/prometheus.rs,data_plane/src/drivers/query/fallback/mod.rs,data_plane/src/drivers/query/servers/http.rs,data_plane/src/drivers/query/servers/mod.rs,data_plane/src/drivers/query/mod.rs,data_plane/src/query_engines/query_forwarding.rs,data_plane/src/query_engines/query_result.rs,data_plane/src/query_engines/request.rs,data_plane/src/utilsdata_plane/src/drivers/query/servers/metrics.rs/metricsendpoint (query request counts and latency)data_plane/src/drivers/query/adapters/victoriametrics_http.rs,data_plane/src/drivers/query/fallback/victoriametrics.rsdata_plane/src/precompute_engine/window_manager.rs,data_plane/src/precompute_engine/ingest_handler.rs,data_plane/src/precompute_engine/maintenance_runtime.rs,data_plane/src/precompute_engine/output_sink.rs,data_plane/src/precompute_engine/raw_dag.rs,data_plane/src/precompute_engine/group_key.rs,data_plane/src/precompute_engine/metrics.rs,data_plane/src/precompute_engine/config.rs,data_plane/src/precompute_engine/engine.rs,data_plane/src/precompute_engine/mod.rs,data_plane/src/precompute_engine/precompute_engine_design_doc.mddata_plane/src/precompute_engine/revisions.rs,data_plane/src/precompute_engine/frame_lineage.rsdata_plane/src/precompute_engine/partitioning.rs,data_plane/src/precompute_engine/series_router.rs,data_plane/src/precompute_engine/subdag_scheduler.rs,data_plane/src/precompute_engine/worker.rsdata_plane/src/precompute_engine/erp_observer.rsdata_plane/src/query_engines/asap_query_engine,data_plane/src/query_engines/mod.rsdata_plane/src/query_engines/routingdata_plane/src/query_engines/asap_clickhouse_query_enginedata_plane/src/storage_engines/sketch_db/sds.rs,data_plane/src/storage_engines/sketch_db/index,data_plane/src/storage_engines/sketch_db/lifecycle,data_plane/src/storage_engines/sketch_db/current_series.rs,data_plane/src/storage_engines/sketch_db/data,data_plane/src/storage_engines/sketch_db/query,data_plane/src/storage_engines/sketch_db/mod.rs,data_plane/src/storage_engines/mod.rs,data_plane/src/storage_engines/traits.rs,data_plane/src/storage_engines/types/precomputed_output.rsSummaryStore(#819)data_plane/src/storage_engines/sketch_db/persistencedata_plane/src/storage_engines/sketch_db/backfilldata_plane/src/storage_engines/sketch_db/accuracy.rs,data_plane/src/storage_engines/sketch_db/metrics.rsdata_plane/src/update_samplingdata_plane/src/tests,data_plane/tests(old files),data_plane/benches,data_plane/examples/promql,data_plane/examples/repeated_window_comparison.rs,data_plane/examples/sketch_db_diag.rs,control_plane/tests/discovery_snapshot.rs,control_plane/tests/snapshot_version.rs,control_plane/tests/o11y_exact_fallback.rs,control_plane/tests/fixtures/o11y_queries.jsoncontrol_plane/data_planetests (this PR and #819–#822)scripts/e2e.shcontrol_plane/.gitignore,data_plane/.gitignore,data_plane/.cargo/config.toml,data_plane/rustfmt.toml,data_plane/LICENSE.gitignore, defaultrustfmtand the rootLICENSEcontrol_plane/Dockerfile,data_plane/Dockerfile,.dockerignore,data_plane/docker-compose.yml.j2,data_plane/query-engine-rust-cli-compose.yml.j2,demos/asapquerytools/autosketch-comparison,tools/erp-figure1,data_plane/examples/autosketch_comparison.rs,data_plane/examples/univmon_erp_artifact.rstools/clickhouse-benefit,tools/o11y-sql-main-evaltools/cluster-data-exporterdocs/01-getting-started,docs/03-how-to-guides,docs/design_docs,docs/developer_docs,docs/evaluation,docs/examples,docs/user_guide,docs/README.md,docs/offline-o11y-final-2026-09-08.md,control_plane/docspyproject.tomland the Python packageHow
plan_workloadlowers the workload withasap_frontend_promql::lower_promql_query_workload, adds each series' identity to PromQL roots withwith_promql_series_identity(a failure is returned asPlanError::SeriesIdentity, as the Planner's executor lowering does), and runsasap_plan_selection::run_stage1_to_stage3withPlanningModels::builtin()plus the backend'sDeploymentCapabilitiesandPlanSelectionSettings. The backend never chooses: it takes Stage 3'sSelection::plan(the selected candidate's timed roots), expands eachPaneChaininto its panes withexpand_pane_chains(the executor's compiler rejects unexpanded chains), and exports it withcompile_physical_asap_workload.run_stage1_to_stage3is used instead of theasap-plannerfacade'se2e_planbecausee2e_planreturnsPlanOutput, whose DAG export resets every node to query time, and because the facade pulls in the SQL frontend (DataFusion), which a PromQL-only backend does not need.The backend declares
query_time_results_retained: false: the data plane does not implementMatAtQueryKeptAcrossEvaluations, so the Planner never offers it.The data plane's write route sets
DefaultBodyLimit::max(limits.max_compressed_bytes), replacing axum's 2 MB default, so bodies up to the Remote Write limit reachremote_write::decodeand larger ones get 413. Forwarding copies every request header except hop-by-hop headers (Connectionand the headers it names,Keep-Alive,Proxy-*,TE,Trailer,Transfer-Encoding,Upgrade),Host,Content-Length, andAccept-Encoding(only the response'sContent-Typeis copied back, so the response must not be compressed). Thereqwestclient's timeout covers sending the request and reading the whole response; a timeout is a 502.Key code interfaces added / modified
control_planeCLI:--workload,--deployment-capabilities(PlannerDeploymentCapabilitiesJSON),--measured-operation-costs(PlannerMeasuredOperationCostlist JSON),--out.data_planeCLI:--plan,--prometheus-url(its help states that Remote Write samples are counted and dropped until the precompute engine lands),--forward-timeout-seconds(default 120),--listen.Before this PR
The backend selected its own plan with its own cost model and engines, on Planner revisions that differed per crate.
After this PR
The plan is the one the Planner selected (for Example 1,
P1: both queries exact at query time). The data plane installs it, counts and drops Remote Write samples, and forwards queries, with their auth and tenancy headers, markedx-asap-execution: forwarded.scripts/sync-asapplanner-main.shupdates the four Planner pins andCargo.locksuccessfully.Usage
Verification
python3 -m unittest discover -s scripts/ci -p 'test_*.py': 5 tests pass. The fakecargonow rejects a-pspec that is not in the repository'sCargo.lock(exit 101, like real Cargo), and a new test runs the sync script against the repository's ownCargo.toml; it failed withpackage ID specification `asap-executor` did not match any packagesbefore the pin was dropped.scripts/sync-asapplanner-main.shrun for real on a scratch copy of the tree: rewrote the four pins and updatedCargo.lockto ASAPPlanner main (synchronized ASAPPlanner dependencies to f9dd41e0...).cargo fmt -p control_plane -p data_plane -- --check;cargo check --workspace --locked;cargo clippy --workspace --all-targets --locked -- -D warnings;cargo test --workspace --locked: all pass (control plane 4 tests, data plane 4 unit + 5 server tests).AuthorizationandX-Scope-OrgIDwere not forwarded (now forwarded, whileHostandKeep-Aliveare not); a failed series-identity rewrite was swallowed (nowquery 0: series identity: ...). A forwarded query slower than the timeout gets 502.x-asap-execution: forwarded, and the stand-in receivedAuthorization: Bearer tandX-Scope-OrgID: tenant-a.node scripts/ci/test_planner_merge.jswas not run locally (local Node v12 has nonode:fs); MVP CI runs it.