Skip to content

feat(sketch-db): Phase 5h — PrometheusReader (real RawSampleReader for backfill) - #35

Merged
zzylol merged 1 commit into
mainfrom
sketchdb/phase5h-prometheus-reader
Apr 18, 2026
Merged

zzylol merged 1 commit into
mainfrom
sketchdb/phase5h-prometheus-reader

Conversation

@zzylol

@zzylol zzylol commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Summary

First real RawSampleReader in-tree. Turns BackfillSource::Prometheus from "factory returns no-reader error" into an actual working read path, so --enable-backfill-worker on a Prometheus-backed deployment now produces real backfills instead of shadow-mode Failed jobs.

  • PrometheusReader::new(base_url) + with_step + with_timeout, wraps /api/v1/query_range.
  • Translates LabelFilter → PromQL matcher with stable key ordering + quote/backslash escape.
  • Parses {status: success, data: {resultType: matrix, result: [{metric, values}]}} into Vec<RawSample>.
  • Errors map cleanly to RawSampleReaderError::{InvalidRange, Upstream, Decode} so the worker marks jobs Failed with a clear error_message.
  • default_reader_factory() routes Prometheus to this reader; other variants still return "not yet implemented" per-variant.
  • main.rs uses default_reader_factory() under --enable-backfill-worker (replacing noop_reader_factory).

Determinism

For raw-ingest deployments (live = Prometheus remote write), step=scrape_interval makes query_range return the same sample sequence live saw; the bit-identical parity test from PR #32 still holds. For DC-ingest deployments, approximate equivalence ε. /api/v1/read (protobuf remote-read) is the upgrade path if we ever need sub-step resolution; trait contract unchanged.

Test plan

  • 8 unit tests: promql synthesis + escape + bare-metric, series-key render variants, fractional-seconds roundtrip, inverted range.
  • 8 integration tests against spawned axum mock Prometheus: happy path, param propagation, PromQL matcher, HTTP 5xx, Prometheus error status, malformed JSON, per-series ordering, empty result, wrong resultType.
  • 677 lib tests pass (up from 661).
  • clippy + fmt clean.

What this unblocks

End-to-end backfill on Prometheus-backed deployments: POST /api/v1/db/backfill with {source: {Prometheus: {url}}}, start backend with --enable-backfill-worker, job goes Queued → Running → Complete with bit-identical per-window precomputes in the store.

S3Gorilla / ClickHouse readers are Phase 5h-2 / 5h-3; OtherSketch is Phase 5i.

🤖 Generated with Claude Code

…r backfill)

First real `RawSampleReader` in-tree. Turns the `BackfillSource::Prometheus
{ url }` variant from "factory returns no-reader error" into an actual
working read path, so `--enable-backfill-worker` against a Prometheus-
backed deployment now produces real backfills instead of shadow-mode
Failed jobs.

## What's landed

`src/stores/sketch_db/prometheus_reader.rs`:

* `PrometheusReader::new(base_url) + with_step(d) + with_timeout(d)` —
  HTTP client wrapper over `/api/v1/query_range`. Step defaults to 15s
  (typical Prometheus scrape interval); overridable per-deployment.
* `impl RawSampleReader` that:
  1. Translates `LabelFilter { metric, equality }` into a PromQL
     matcher `metric{k1="v1",k2="v2"}` with stable key ordering and
     proper escape for quotes/backslashes/newlines.
  2. Issues a `GET /api/v1/query_range` with `query`, `start`, `end`,
     `step` params.
  3. Parses the `{status: "success", data: {resultType: "matrix",
     result: [{metric, values}]}}` JSON shape into `Vec<RawSample>`.
  4. Maps every failure into `RawSampleReaderError::{InvalidRange,
     Upstream, Decode}` so the worker can surface the root cause on
     `BackfillJob::error_message`.
* `ms_to_fractional_seconds` / `fractional_seconds_to_ms` helpers for
  the ms ↔ Prometheus-fractional-seconds conversion.
* `render_series_key` rebuilds `metric{labels...}` from Prometheus's
  response metric map so downstream grouping and `extract_group_key`
  see the same shape they see for live samples.

`backfill_service::default_reader_factory()`:

* Routes `BackfillSource::Prometheus { url }` to `PrometheusReader::new`.
* Returns a clear "reader for <source> not yet implemented" error for
  `S3Gorilla` / `ClickHouse` / `OtherSketch` so the controller sees
  exactly which reader is missing.

`main.rs` now uses `default_reader_factory()` under
`--enable-backfill-worker` instead of `noop_reader_factory`, so the
service actually does work against Prometheus targets.

## On determinism (§10.5)

`/api/v1/query_range` with step = scrape_interval returns one point
per step per series. For deployments whose live ingest is Prometheus
remote write (backend-side sketch-core construction), this gives the
same sample sequence live saw, so the bit-identical parity test from
PR #32 still holds. For deployments whose live ingest goes through
DC's OTLP sketch-building path, approximate equivalence holds; exact
bit-identical would need `/api/v1/read` + sketchlib-go parity work.

Module doc flags that `/api/v1/read` (the protobuf remote-read
protocol) is the upgrade path if we ever need sub-scrape-interval
resolution; trait contract is unchanged.

## Test plan

- [x] 8 unit tests: `build_promql` ordering + escape + bare-metric,
  `render_series_key` ordering + bare + fallback, fractional-seconds
  round-trip, inverted-range → `InvalidRange`.
- [x] 8 integration tests against a spawned axum mock Prometheus:
  happy-path multi-series parse + param propagation, PromQL matcher
  synthesis, HTTP 5xx → Upstream, Prometheus `status: error` →
  Upstream, malformed JSON → Decode, ingest-order preservation
  per series, empty result, wrong `resultType` → Decode.
- [x] 677 lib tests pass (up from 661).
- [x] clippy `--workspace --all-targets --tests -- -D warnings` clean.
- [x] `cargo fmt -- --check` clean.

## What this unblocks

A Prometheus-backed deployment can now:

1. Create a backfill job via `POST /api/v1/db/backfill` with
   `source: {Prometheus: {url: "http://prom:9090"}}`.
2. Start the backend with `--enable-backfill-worker`.
3. Watch the job transition `Queued → Running → Complete` in
   `/api/v1/db/backfill/jobs/<id>`, with per-window precomputes
   written to the store bit-identically to what live ingest would
   have produced.

`S3Gorilla` and `ClickHouse` readers are the remaining Phase 5h-2 /
5h-3 work; `OtherSketch` (lossless widening from an existing sketch)
is Phase 5i.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@zzylol
zzylol merged commit e932447 into main Apr 18, 2026
@zzylol
zzylol deleted the sketchdb/phase5h-prometheus-reader branch April 18, 2026 17:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant