Skip to content

feat(data-plane): read query-time raw windows from a local raw buffer and Prometheus remote read - #831

Draft
zzylol wants to merge 2 commits into
feat/per-sample-ingestionfrom
feat/local-raw-windows
Draft

zzylol wants to merge 2 commits into
feat/per-sample-ingestionfrom
feat/local-raw-windows

Conversation

@zzylol

@zzylol zzylol commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Stack 4/4. Base: #830.

Why

Every query-time raw window, and every missing kept pane, was an instant range-selector query to Prometheus decoded as JSON. With raw data retained that was 15.5% of the data plane's CPU (146 MB of JSON per 90 minutes at 2,000 series). User decision (Stage D): query-time raw windows come from a local in-memory ring buffer, fed and sharded like ingestion and sized by the plan. Anything older comes from Prometheus remote read (protobuf), not the JSON API.

What

  • Local raw buffer. Each ingestion worker appends its shard's samples. Each metric the plan reads at query time is kept, back from the newest sample, for that metric's longest query-time read: the largest end_offset + width of its query_time_raw_windows, and of its panes built at query time and kept (their retention, which covers the declared range evaluations). Metrics no raw window reads are not buffered. --raw-buffer-ms overrides the horizon; 0 keeps none. (A first commit used one horizon, the plan's longest read, for every metric. The second sizes it per metric, which is the plan's longest read applied to each metric; the table shows both.)
  • Reads. A raw window is read from the buffer. The part before the buffer's cut, its first sample or the trim point of any shard, is read with Prometheus remote read (/api/v1/read, snappy protobuf, sampled response). The JSON raw fetch is removed. Forwarding of unplanned queries is unchanged.
  • The data plane logs the buffer's series and samples every 30 s.

How

RawBuffer holds, per shard, the series' samples by metric and label set in time order, under a read-write lock, and trims each metric to its horizon. A read takes each shard's read lock, collects the samples in (start, end] and takes the latest trim point over the shards as the cut. Samples at or before the cut come from remote read, so a shard trimmed during the read cannot leave a gap.

Before this PR

Raw windows: one JSON instant query per window or missing kept pane, decoded through serde_json::Value.

After this PR

A data plane that has received the samples since it started answers raw windows from memory, with no Prometheus request. Remote read covers only what predates its first sample or its horizon.

Verification

  • cargo test --workspace passes, including e2e_examples (10), answers, range, kept.
  • New e2e_examples::example1_raw_windows_from_the_buffer_and_remote_read, failing on feat(precompute): update each open pane's summary state per sample #830. Prometheus holds samples from 0 and the data plane receives them from 4:45. At 5 min the 1-minute window takes 4:15–4:45 from remote read and 5:00 from the buffer. At 10 min the window is the buffer's alone, with 0 remote reads. Every answer equals the Planner executor's over all samples, and the JSON API is never used for raw samples.
  • New raw_buffer tests: reads equal the samples of the metric in (start, end]; a read past the horizon splits at the cut; samples before startup come from Prometheus.
  • The kept-pane and range e2e tests that count raw reads now run with --raw-buffer-ms 0, so they count remote reads; the counts are unchanged (5, 1, 1 for B3; 1 per range query).

Benefit harness

Same setup as #828. Each run writes every sample to the data plane from the start, so the buffer covers every raw window and no remote read happens.

s10

Run Prometheus core-s Data plane core-s Data plane peak RSS GiB rate p50/p99 ms topk p50/p99 ms p99[5m] p50/p99 ms p99[60m] p50/p99 ms forwarded
Prometheus alone 19.5 - - (Prometheus 0.09) 10.1 / 19.7 10.2 / 19.4 20.6 / 30.7 110.1 / 140.0 -
pr3 20.4 43.9 0.48 41.7 / 54.5 40.4 / 50.9 136.5 / 151.1 847.7 / 1004.6 0
pr4-single-horizon 5.7 37.5 0.50 25.2 / 36.1 22.8 / 34.6 87.7 / 103.3 574.2 / 654.2 0
pr4 5.4 36.6 0.48 24.0 / 45.0 21.3 / 42.9 86.2 / 141.3 564.7 / 689.7 0

s10-noraw

Run Prometheus core-s Data plane core-s Data plane peak RSS GiB rate p50/p99 ms topk p50/p99 ms p99[5m] p50/p99 ms p99[60m] p50/p99 ms forwarded
Prometheus alone 18.9 - - (Prometheus 0.09) 9.8 / 18.2 10.0 / 17.3 20.2 / 29.8 94.7 / 116.7 -
pr3 10.1 31.5 0.31 26.2 / 45.0 19.8 / 26.6 72.7 / 87.2 110.6 / 136.8 325
pr4-single-horizon 9.9 31.1 0.30 25.1 / 45.6 19.1 / 25.0 72.5 / 87.5 86.9 / 118.0 325
pr4 10.1 31.7 0.30 25.3 / 48.1 20.3 / 25.3 73.9 / 87.4 96.2 / 133.3 325

s50

Run Prometheus core-s Data plane core-s Data plane peak RSS GiB rate p50/p99 ms topk p50/p99 ms p99[5m] p50/p99 ms p99[60m] p50/p99 ms forwarded
Prometheus alone 85.7 - - (Prometheus 0.14) 43.0 / 78.1 43.6 / 71.0 95.1 / 139.3 457.8 / 685.3 -
pr3 98.0 173.0 1.75 166.0 / 223.2 158.1 / 207.4 582.5 / 651.9 4036.9 / 4685.9 0
pr4-single-horizon 27.7 158.3 1.71 96.2 / 179.3 92.2 / 170.4 381.3 / 551.0 3176.0 / 3274.3 0
pr4 27.6 152.6 1.76 90.5 / 131.6 88.8 / 118.8 385.3 / 435.3 2892.5 / 3287.0 0

s50-noraw

Run Prometheus core-s Data plane core-s Data plane peak RSS GiB rate p50/p99 ms topk p50/p99 ms p99[5m] p50/p99 ms p99[60m] p50/p99 ms forwarded
Prometheus alone 88.9 - - (Prometheus 0.14) 44.9 / 74.6 45.1 / 80.9 93.9 / 138.1 396.1 / 525.6 -
pr3 46.1 130.0 0.98 106.5 / 285.2 64.0 / 214.4 310.5 / 876.1 452.5 / 533.2 325
pr4-single-horizon 47.8 134.3 1.02 106.5 / 195.8 65.7 / 99.2 317.2 / 362.3 434.7 / 598.0 325
pr4 47.1 133.6 0.97 105.6 / 202.9 64.4 / 96.2 320.8 / 368.4 470.4 / 594.5 325

"pr4-single-horizon" is this PR's first commit (one horizon for every metric); "pr4" is its head (per-metric horizons). "Prometheus alone" is the baseline of the pr3 run.

CPU saved against the JSON raw fetch (raw data retained; head against pr3):

  • s10: data plane 43.9 → 36.6 core-s; its Prometheus 20.4 → 5.4 core-s, which no longer answers raw range queries.
  • s50: data plane 173.0 → 152.6 core-s; its Prometheus 98.0 → 27.6 core-s.
  • Without raw data the plan reads nothing raw at query time, so those runs are unchanged within noise.

Raw buffer size, at its largest, from the data plane's log. The plan's horizons are http_requests_total 1 min and latency_ms 60 min.

Config One horizon (60 min, every metric) Per-metric horizons (head)
s10 (2,000 series) 480,000 samples, ≈ 7.7 MB at 16 B/sample 244,000 samples, ≈ 3.9 MB
s50 (10,000 series) 2,400,000 samples, ≈ 38 MB 1,220,000 samples, ≈ 20 MB

The bytes are the samples' 16 bytes each; per-series map entries come on top. Peak RSS does not change measurably between pr3 and pr4.

… and Prometheus remote read

- A local raw buffer keeps the recent samples Remote Write delivered,
  sharded like ingestion: each ingestion worker appends its shard. It keeps
  the plan's longest query-time raw read back from the newest sample: the
  largest `end_offset + width` of `query_time_raw_windows` and of the panes
  built at query time and kept (their retention, which covers range
  evaluations). `--raw-buffer-ms` overrides it; 0 keeps none.
- A raw window is read from the buffer; the part before the buffer's cut
  (its first sample, or the trimmed horizon of any shard) is read with
  Prometheus remote read (protobuf, `/api/v1/read`). The JSON query API is
  no longer used for raw samples. Forwarding of unplanned queries is
  unchanged.
- The data plane logs the buffer's series and samples every 30 s.
…est query-time read

The buffer kept every metric for the plan's single longest raw read: in the
benefit plan, 60 minutes of the counter whose raw reads span one minute,
and of metrics no raw window reads. It now keeps only the metrics the plan
reads at query time, each for its own longest read (query-time raw windows,
and panes built at query time and kept).
@zzylol zzylol added enhancement New feature or request area: data plane Runtime ingestion, storage, and query-serving data paths area: query engine PromQL lowering, execution, result shaping, and fallback behavior test: end-to-end Process-boundary, differential, conformance, and full-stack tests labels Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: data plane Runtime ingestion, storage, and query-serving data paths area: query engine PromQL lowering, execution, result shaping, and fallback behavior enhancement New feature or request test: end-to-end Process-boundary, differential, conformance, and full-stack tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant