Repository navigation
feat(query): answer planned instant queries by running the plan's roots - #821
Merged
Merged
Conversation
This was referenced Oct 7, 2026
zzylol
force-pushed
the
feat/precompute-engine
branch
from
October 18, 2026 15:40
eb8a314 to
f9c3343
Compare
zzylol
force-pushed
the
feat/query-engine
branch
from
October 18, 2026 15:40
6f7b53c to
b1b1e5a
Compare
This was referenced Oct 18, 2026
zzylol
force-pushed
the
feat/precompute-engine
branch
from
October 20, 2026 16:09
f9c3343 to
b23b37e
Compare
zzylol
force-pushed
the
feat/query-engine
branch
from
October 20, 2026 20:06
b1b1e5a to
2142441
Compare
zzylol
changed the base branch from
feat/precompute-engine
to
feat/installable-plan
October 20, 2026 20:06
zzylol
force-pushed
the
feat/query-engine
branch
from
October 20, 2026 23:26
2142441 to
1bdf7e9
Compare
Each plan root (InstallablePlan.queries) is compiled once with two kinds of inputs: stored outputs, bound at evaluation time T to their definition's summary ending at T - offset, and the plan's query-time raw windows (query_time_raw_windows), bound to the metric's samples in (T - offset - width, T - offset] fetched from Prometheus and selected by the window's RawSampleSelector. An instant query whose text is a planned query is answered with a Prometheus vector and x-asap-execution: plan. Unplanned queries are forwarded; planned ones that cannot be answered (summary missing or off the coverage grid, fetch or execution failure) are forwarded with x-asap-forward-reason. Raw-window reads carry the caller's end-to-end headers (Authorization, X-Scope-OrgID and the rest query forwarding keeps), so Prometheus authorizes and scopes them as it would the caller's own query. Answer labels come from series_labels_of_output_row.
- A query whose time is not finite or beyond MAX_TIME_MS gets 400 bad_data, as Prometheus answers; window and pane binding use checked arithmetic, so no evaluation time can overflow (time=-1e300 panicked the engine thread in debug builds). - Raw-window fetches select only the series the plan keeps: the Scan's label matchers PromQL can express go into the range selector, with metric and values escaped. The selector still applies every matcher. - Raw windows are fetched concurrently. - A forward reason with characters a header cannot carry is sanitized instead of dropped.
A plan that keeps query-run summaries installs, but this data plane does not build them yet, so queries reading them are forwarded. Say so in the log at startup.
zzylol
force-pushed
the
feat/query-engine
branch
from
October 20, 2026 23:46
1bdf7e9 to
afa5bc6
Compare
zzylol
marked this pull request as ready for review
October 20, 2026 23:46
Stored outputs built in a QueryRun (MatAtQueryKeptAcrossEvaluations) and in an IngestionRun are named as the Planner names them, in the startup warning, errors, docs and test names.
… bound raw-sample window RawRange becomes BoundRawSampleWindow and QueryEngine::raw_ranges becomes bound_raw_sample_windows, the design doc's term for a plan RawSampleWindow bound to one evaluation time.
This was referenced Oct 21, 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.
Base: main (#838 merged). Next: #824 (stored outputs built in a
QueryRun).Why
Every query was forwarded to Prometheus, so the selected plan never answered anything.
What
A planned instant query is answered by running its root of the DAG at evaluation time
T:T − offset.query_time_raw_windows. For each one, the data plane fetches(T − end_offset − width, T − end_offset]from Prometheus and selects rows with the window'sRawSampleSelector(theScan's label matchers, and for an instant selector each series' latest sample).Scanlabel matchers that PromQL can express (a label compared with=,!=,=~or!~to a text literal) go into the range selector, so Prometheus returns only the series the plan keeps. The metric and values are escaped as PromQL strings. TheRawSampleSelectorstill applies every matcher, so a matcher left out only widens the fetch. Raw windows are fetched concurrently.timethat is not finite or beyondMAX_TIME_MS(i64::MAX / 4ms) gets 400bad_data, as Prometheus answers. Window and pane binding use checked arithmetic, so no evaluation time overflows.AuthorizationandX-Scope-OrgID. Prometheus then authorizes and scopes the read as it would the caller's own query.Content-Typeis dropped, because the read is a GET without the caller's body.asap_executor::series_labels_of_output_row.x-asap-execution: plan. Any other query, including range queries, is forwarded withx-asap-execution: forwarded. A planned query that can't be answered gives its reason inx-asap-forward-reason: a missing pane,Toff the pane grid, or an unsupported shape.MatAtQuerynodes run likeNotMaterializedones: each instant query is its own evaluation, so there is nothing to share within it.How
Each root (
InstallablePlan.queries[i].root_node_id) is compiled once at install time withasap_executor::physical_planner::compile, with its stored outputs and the plan's raw windows as bounded input contracts. ATimeRangethe plan lists no window for makes the root unsupported, and its query is forwarded with that reason. At query time the engine thread checks the panes, the HTTP side fetches the bound raw-sample windows, and the engine instantiates the compiled root over both and runs it under query scope.Key code interfaces added / modified
Before this PR
Every query was forwarded to Prometheus.
After this PR
Example 4b without raw data answers
quantile_over_time(0.99, latency_ms[60m])at 70 min from six stored panes, reading no bound raw-sample window; Example 1 answers both queries from bound raw-sample windows fetched from Prometheus.Usage
Over HTTP the same query returns a Prometheus vector with
x-asap-execution: plan(data_plane/tests/answers.rs).Limitations
Ton the pane grid.Sample, becauseRawSampleSelector::window_batchtakes owned label maps. This is the same ASAPPlanner follow-up noted in feat(precompute): build stored summary panes from Remote Write samples #820.QueryRun(StoredOutputBuild::QueryRun) are not built yet: a query that reads one is forwarded, and the data plane logs a warning at startup when the plan has any. feat(query): build stored outputs built in a QueryRun on first use #824 builds them.Verification
data_plane/tests/query.rs(6 tests):examples_answer_like_the_query_time_reference: every query of examples 1, 3b and 4b, with and without raw data, compared againstInstallablePlan::query_time_referenceover the same samples.extreme_evaluation_times_are_not_answered:i64::MINis refused, andi64::MAXdoes not panic.bound_raw_sample_windows_carry_the_scans_label_matchers.data_plane/tests/answers.rs:rejects_evaluation_times_out_of_rangecovers-1e300,1e300,NaNandinf, each answered with 400bad_data, with the engine still running. Before this fix,-1e300panicked the engine thread, so the response was 502 and ingestion stopped.parses_instant_query_parameters(out-of-range times),builds_escaped_range_selectors,sanitizes_header_values.data_plane/tests/answers.rs(4 tests) over HTTP against a stand-in Prometheus: Example 1 from raw samples, including the left-open range start; Example 4b from panes built through Remote Write; andraw_range_reads_forward_auth_and_tenancy_headers. In that last test the stand-in Prometheus answers 401 withoutAuthorizationandX-Scope-OrgID. With the headers the plan answers; without them the raw read fails and the query is forwarded with the reason.--locked). All passed. The Usage output is from a scratch run on this tree.