Repository navigation
feat(precompute): build stored summary panes from Remote Write samples - #820
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
An engine thread owns the installed plan (Planner IR holds Rcs) and receives Remote Write samples from the HTTP server. For each stored summary definition it buffers its Scan metric's samples by tumbling pane (end - width, end], like PromQL ranges. When the watermark (newest sample minus --allowed-lateness-ms) passes a pane's end, it selects the pane's rows with the executor's RawSampleSelector (the Scan's label matchers) and runs the definition's build from its TimeRange input, then stores the output. Panes without samples are stored empty; the first pane seen is skipped because it may be partial; samples for built panes are dropped and counted. A full engine queue answers Remote Write with 503 so senders retry. The SDS finds the plan's materialization frontier itself from each node's timing (Sds::ingestion_frontier), since the executor exports frontier_from_timing only for tests; a test checks both agree on every example. SummaryStore keeps panes per definition, so a lookup borrows the definition id and eviction splits off the panes still needed. Forwarded queries keep main's timeout (--forward-timeout-seconds, through DataPlane::with_forward_timeout), header forwarding and Remote Write body limit.
zzylol
force-pushed
the
feat/precompute-engine
branch
from
October 20, 2026 16:09
f9c3343 to
b23b37e
Compare
zzylol
marked this pull request as ready for review
October 20, 2026 16:09
- One future timestamp no longer closes every pane up to it: only samples of a metric a stored summary reads move the watermark, samples more than --max-future-ms (default 5 min) ahead of the wall clock are dropped and counted, and when the watermark jumps the panes that would already be evicted are skipped instead of built. Pane arithmetic is checked, so timestamps near i64::MIN/MAX neither overflow nor loop. - Pin ASAPPlanner main ecea897f, whose RawSampleSelector drops stale markers (a range window keeps the samples around one; an instant selector whose latest sample is one has no row). The HTTP-layer stale filter is removed. - A stopped engine thread makes Remote Write answer 500 and /-/ready 503, instead of 503 forever. - The engine queue is bounded by queued samples (--max-queued-samples, default 2,000,000); beyond it Remote Write gets 503. - /api/v1/status/asap reports the engine's counters. Sample counters count each sample once: accepted, dropped late, skipped with the first pane, dropped as future, unrouted; pane counters count built, failed and skipped-as-expired panes. - --allowed-lateness-ms is unsigned, and a zero-width pane is refused. - The plan is parsed once, on the engine thread. run_root executes one root; the selector's unused end offset is 0.
AtomicU64::fetch_update is deprecated on current stable (renamed try_update), which fails clippy -D warnings in CI.
…mpty queue - The watermark is the newest routed sample capped at the wall clock, minus the allowed lateness: one series stamped ahead of the others (but within --max-future-ms) no longer closes panes early and drops on-time samples as late. - A request is always admitted into an empty queue, so a --max-queued-samples smaller than one request cannot refuse it forever. - A sample of a metric no definition reads counts as unrouted even when it is beyond the future bound. - Remote Write requests without samples are not queued. - EngineHandle::stop is test-only (doc(hidden)); a test covers the running flag clearing when the engine thread panics. - The default retention's doubling of the pane width is overflow-checked.
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 (#819 merged). Next: #821.
Retention and stored outputs still come from the backend's own frontier and "largest offset plus two panes". The next PR (M5, stacked on this one) switches the data plane to
InstallablePlan.stored_outputsand itsretention_ms.Why
Remote Write samples were accepted and dropped, so no stored summary was ever built.
What
A precompute engine on the engine thread builds stored panes from Remote Write:
Sds::ingestion_frontierfinds the outputs to store from each node's timing: ingestion-time nodes read by a query-time node or as a query's answer. At the pinned ASAPPlanner revision the executor exportsfrontier_from_timingonly undertest-support, so the data plane no longer calls it; a test checks the two agree on every example, with and without raw data.RawSampleSelector, built from the definition'sScanandTimeRange, turns a pane's samples into the build's input, so the plan's label matchers apply.(end − width, end](left-open, like PromQL ranges and the executor's windows) is built once the watermark passesend. The watermark is the newest sample of a metric some definition reads, capped at the wall clock, minus the allowed lateness, so one series stamped ahead of the others cannot close panes early. The build runs the definition's builder, compiled from the DAG withasap_executor::physical_planner::compile, under ingestion scope, and stores the summary batch. Panes without samples are stored empty.--max-future-ms(default 5 min) ahead of the wall clock is dropped and counted. When the watermark jumps, panes that would already be evicted are skipped and counted, not built. Pane arithmetic is checked, so timestamps neari64::MIN/MAXneither overflow nor loop.ecea897f, whoseRawSampleSelectordrops Prometheus stale markers: a range window keeps the samples around one, and an instant selector whose latest sample is one has no row. The data plane passes markers through, and the HTTP-layer filter is removed. The same pin bump brings in ASAPPlanner test: select, install and execute every admitted query-ensemble candidate #775: the executor runs exact per-series range reductions (quantile/sum/avg/min/max/count_over_time), and Stage 3 rejects per-entity aggregate candidates the executor cannot run.--max-queued-samples, default 2,000,000)./api/v1/writereturns 503 when admitting a request would exceed the bound, so senders retry. A request into an empty queue is always admitted, so a bound smaller than one request cannot refuse it forever. A request without samples is not queued. If the engine thread has stopped, it returns 500 and/-/readyreturns 503./api/v1/status/asapreturns JSON withengine_running,samples_queuedand the precompute counters. Eachsamples_*counter counts a sample once:accepted,dropped_late,skipped_first_pane,dropped_future,unrouted. Thepanes_*counters countbuilt,failedandskipped_expiredpanes.data_plane --allowed-lateness-ms(default 15 s, unsigned). A zero-width pane is refused when the engine is built.SummaryStorekeepsBTreeMap<DefinitionId, BTreeMap<i64, S>>.getborrows the definition id instead of building a key, andevict_throughusessplit_offon one definition's panes instead of scanning every pane.DataPlane::newgives forwarded queries the default 120 s timeout;with_forward_timeoutchanges it, anddata_plane --forward-timeout-secondssets it. Header forwarding and the Remote Write body limit are unchanged.How
The Planner IR holds
Rcs, so one engine thread parses the plan once and owns it, the precompute engine and the store. HTTP handlers send it batches over a channel; a shared counter of queued samples bounds the queue, and the engine publishes its counters after each batch.Known cost:
window_batchtakesSamples with owned label maps, so each sample's labels are cloned once per pane build. An ASAPPlanner follow-up could take borrowed or interned labels; the series interning later in this stack does not remove this copy.Key code interfaces added / modified
Before this PR
Remote Write samples were decoded and dropped.
After this PR
Example 4b without raw data, one series sampled every 15 s from 1 to 75 min: the pane ending at 10 min is skipped as possibly partial, and panes ending at 20..70 min are built.
Usage
Verification
data_plane/tests/precompute.rs(14 tests): the watermark and skipped first pane, ignored metrics and dropped late samples, empty panes, label matching, left-open boundaries ofpane_end, and the engine putting a sample at exactly 20 min into the pane(10, 20]and one at 20 min + 1 ms into(20, 30]. Added with the review fixes:unrouted_future_sample_does_not_close_panesis the review's repro: another_metricsample 30 days ahead, then a real sample. It failed on the previous head (4,320 panes built instead of 2) and passes now.drops_samples_beyond_the_future_boundwatermark_jump_builds_only_retained_panes: a 30-day jump builds the 7 retained panes and skips the rest.extreme_timestamps_do_not_overflowrejects_zero_width_panesevicts_panes_past_retentionaccepts_out_of_order_samples_within_latenessstale_markers_are_not_samplesseries_ahead_of_the_clock_does_not_close_panes_early: the re-review's repro (wall clock base+9m, one series at +13m). It failed onac6cec1b, where the on-time sample at base+9m was dropped as late, and passes now.far_future_unrouted_sample_counts_as_unrouted(failed onac6cec1b)data_plane/src/engine.rsunit tests:admission(always into an empty queue,Busyotherwise past the bound) andrunning_guard_clears_on_panic.data_plane/tests/sds.rs:ingestion_frontier_matches_the_executor(examples 1, 3b, 4b, with and without raw data) and the store test against the new layout.data_plane/tests/server.rs: main's body-limit, header-forwarding and timeout tests run against the engine-backed data plane. New:admits_a_large_request_into_an_empty_queue,does_not_queue_empty_requests,stopped_engine_fails_writes_and_readiness(500,/-/ready503) andreports_sample_counters(/api/v1/status/asap).node scripts/ci/test_planner_merge.js,cargo fmt --check,cargo check,cargo clippy --all-targets -D warningsandcargo test --workspace, all--locked. All passed.