Skip to content

refactor(sketch-db): Phase 3b-2-a — extract forced-agg-id context builder - #37

Merged
zzylol merged 1 commit into
mainfrom
sketchdb/phase3b-2-a-refactor
Apr 18, 2026
Merged

zzylol merged 1 commit into
mainfrom
sketchdb/phase3b-2-a-refactor

Conversation

@zzylol

@zzylol zzylol commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Pure refactor. Splits build_query_execution_context_promql into composable pieces so Phase 3b-2-b can build per-segment execution contexts against specific agg_ids. Zero behavioural change to existing code paths.

  • parse_and_match_promql — shared phase-1 (parse + pattern match).
  • resolve_agg_info_promql — existing phase-2 auto-resolver, now named + extracted.
  • agg_info_from_forced_id(agg_id) — new: look up agg type in StreamingConfig, return single-aggregation AggregationIdInfo, None if id missing.
  • New entry point: build_query_execution_context_promql_for_agg_id(query, time, forced_agg_id) — skips auto-resolver, uses the forced id.

What's NOT changed

  • Original build_query_execution_context_promql now delegates to helpers but is body-by-body identical in effect.
  • build_promql_execution_context_tail, execute_context, execute_range_query_pipeline — untouched.
  • All existing query paths continue through the auto-resolver.

Test plan

  • 4 new unit tests: auto-resolve still works, forced id produces context, unknown id → None, forced and auto agree for single-agg case.
  • 696 lib tests pass (up from 692). Existing dispatch_arithmetic_tests + plan_execution_temporal_tests green.
  • clippy + fmt clean.

Next

Phase 3b-2-b wires per-segment dispatch:

  1. Timeline lookup at query entry.
  2. 1 segment → existing path. > 1 segment → per-segment forced-id builds + combine_statistic.

🤖 Generated with Claude Code

…lder

Pure refactor. Splits `SimpleEngine::build_query_execution_context_promql`
into composable pieces so Phase 3b-2-b's timeline dispatch can build
per-segment execution contexts against specific `agg_id`s without
re-running the auto-resolver. Zero behavioural change to existing
code paths.

## What's extracted

* `parse_and_match_promql(query) -> Option<(QueryPatternType, PromQLMatchResult)>`
  — shared phase-1 (PromQL parse + pattern match). Previously
  inlined inside `build_query_execution_context_promql`.
* `resolve_agg_info_promql(query, match_result, pattern_type) ->
  Option<AggregationIdInfo>` — phase-2 auto-resolver (try
  `QueryConfig` exact match, fall back to capability matching
  with miss-notify). Previously inlined inside
  `build_query_execution_context_promql`.
* `agg_info_from_forced_id(agg_id) -> Option<AggregationIdInfo>`
  — the new building block that powers per-segment dispatch.
  Looks up the agg type in the current `StreamingConfig` and
  returns the single-aggregation `AggregationIdInfo` shape
  (key and value share the same id), matching the fallback
  branch in `get_aggregation_id_info`. Returns `None` if the
  agg_id has been removed from the config.

## What's new

* `build_query_execution_context_promql_for_agg_id(query, time,
  forced_agg_id) -> Option<QueryExecutionContext>` — parallel
  entry point that does phase-1 + `agg_info_from_forced_id` +
  phase-3 (`build_promql_execution_context_tail`), skipping the
  auto-resolver entirely. This is the hook Phase 3b-2-b calls
  per `TimelineSegment`.

## What's NOT changed

* `build_query_execution_context_promql` — now delegates to
  the extracted helpers but body-by-body identical in effect.
  Caller-observable output unchanged.
* `build_promql_execution_context_tail` — untouched (already
  agg-info-agnostic).
* `execute_context` / `execute_range_query_pipeline` — untouched.
* All existing query paths (`handle_query_promql`,
  `handle_range_query_promql`, etc.) — continue through the
  auto-resolver entry point with zero change.

## Test plan

- [x] 4 new unit tests in `forced_agg_id_tests`:
  * Auto-resolve still works after refactor (regression gate).
  * Forced entry point produces a context for a known agg_id.
  * Unknown agg_id returns `None` without panic.
  * Forced and auto-resolved produce observably identical
    `AggregationIdInfo` for the common single-agg case.
- [x] 696 lib tests pass (up from 692); existing
  `dispatch_arithmetic_tests::test_handle_query_promql_single_metric_still_works`
  and `plan_execution_temporal_tests::test_temporal_sum_over_time_*`
  all still green.
- [x] `cargo clippy --workspace --all-targets --tests -- -D warnings` clean.
- [x] `cargo fmt -- --check` clean.

## Next

Phase 3b-2-b wires the per-segment dispatch:
1. At query entry, call `SchemaRegistry::timeline_for_metric(metric, start, end)`.
2. If exactly 1 segment, delegate to the existing auto-resolver
   (zero behaviour change for single-schema queries).
3. If > 1 segment: for each segment, call
   `build_query_execution_context_promql_for_agg_id` with the
   segment's `agg_id` and clipped range, execute, collect the
   scalar, and combine via `combine_statistic`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@zzylol
zzylol merged commit 9acae2a into main Apr 18, 2026
@zzylol
zzylol deleted the sketchdb/phase3b-2-a-refactor branch April 18, 2026 18:32
zzylol added a commit that referenced this pull request Apr 20, 2026
…able stats (#48)

Task #34 gap #1 of 3. The §7 schema-timeline primitive
(`SchemaRegistry::timeline_for_metric`) and the cross-schema
combiner (`engines::timeline_dispatch::combine_statistic`)
landed in PRs #20 / #22 / #25, but `SimpleEngine::handle_query_promql`
was still resolving a single `agg_id` via `resolve_agg_info_promql`
and running the full query against it. A query whose time range
spans a reconfigure boundary (old `agg_id` retired, new
`agg_id` created) saw a data cliff for the pre-boundary slice.

This PR wires the dispatcher:

* New `SimpleEngine::try_handle_query_promql_via_timeline`:
  1. Parse + pattern-match the query, extract metric name.
  2. Build a probe `QueryExecutionContext` to read the
     resolved `[t1, t2]` + `Statistic`.
  3. Call `timeline_for_query(metric, t1, t2)`. Bail out
     with `None` (fall-through to default single-agg path)
     if fewer than two segments, or if the statistic is
     non-combinable (quantile / topk / cardinality / rate
     / increase — those follow in PR B2 with a Partial
     HTTP response surface).
  4. Per segment: reuse `build_query_execution_context_promql_for_agg_id`
     from PR #37 (the extracted forced-agg-id entry point),
     clip the store plan's `[start, end]` to the segment's
     bounds, execute, collect results.
  5. Group by label-tuple and fold per-group per-segment
     scalars through `combine_statistic`. Emit the combined
     scalar as an `InstantVectorElement`. Purged segments
     or segments whose `agg_id` is no longer in the config
     go into `unresolved` so the combiner sees them.

* `handle_query_promql` now tries the timeline path first;
  returns immediately on `Some`, falls through to the
  existing single-agg path on `None`. Zero behavior change
  when the timeline has 0–1 segments for the query's metric
  (the common case today).

## Scope

Combinable stats only: Count / Sum / Min / Max. Non-combinable
stats still take the single-agg path — PR B2 will surface
`CombinedResult::Partial` on the HTTP response so users see
`{covered, missing: [segments]}` explicitly instead of a
silent data cliff.

## Validation

- `cargo test -p query_engine_rust --lib` — 728 pass (baseline
  unchanged; the dispatcher stays dormant when tests only
  register one schema per metric).
- `cargo clippy --all-targets -- -D warnings` — clean
- `cargo fmt --all -- --check` — clean

## Follow-ups (explicit non-scope here)

- **Integration test** seeding two agg_ids + cross-boundary
  Sum query. Requires the full `PrecomputeEngine` setup
  harness the existing e2e tests use; deferred as a
  dedicated PR so this one stays a focused dispatcher
  patch.
- **PR B2**: Partial response surface for non-combinable
  stats on the HTTP adapter.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
zzylol added a commit that referenced this pull request May 2, 2026
* docs(todo): sync after 2026-05-01 query-path-closes-the-loop work (#72)

Capture this session's merged backend work — #70 (tonic OTLP
gRPC max_decoding_message_size bumped to 64 MiB) and #71 (store
range_query_into overlap filter + engine closest-pane selection
+ Prometheus-adapter precompute_window annotation) — at the top
of TODO.md so the runtime-warm-tier-actually-works claim is
testable from the doc.

Cited the matching collector-side PRs (ASAPCollector#210 +
#211) in the companion-changes note so future readers can see
both halves of the wire fix.

Added one new entry under "Known reconciliation gap":
`IngestState.sketch_snapshots` is RAM-only, so backend restarts
break delta ingest until the agent restarts too. Same item is
mirrored in the collector's PROGRESS.md follow-up list — fix on
either side closes the gap.

`_Last updated_` set to 2026-05-01.

Docs only; no code changes.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor: retire sketch-core mirror (#73)

* refactor: retire sketch-core mirror

* refactor: switch consumer imports to asap_sketchlib::sketches::*

Update PR #73 against the reorganized asap_sketchlib (PR #36): the
runtime sketches no longer live under a dedicated `asap::` module — they
were merged into the existing `src/sketches/` layout (single home per
sketch concept, ASAP-runtime types appended to the file that already
holds the high-throughput in-process variant).

Mechanical path swaps in asap-query-engine:
- `asap_sketchlib::asap::dd_sketch::*`           → `::sketches::ddsketch::*`
- `asap_sketchlib::asap::count_min::*`           → `::sketches::countmin::*`
- `asap_sketchlib::asap::count_sketch::*`        → `::sketches::count::*`
- `asap_sketchlib::asap::hll_sketch::*`          → `::sketches::hll::*`
- `asap_sketchlib::asap::kll::*`                 → `::sketches::kll::*`
- `asap_sketchlib::asap::count_min_with_heap::*` → `::sketches::cms_heap::*`
- `asap_sketchlib::asap::hydra_kll::*`           → `::sketches::hydra_kll::*`
- `asap_sketchlib::asap::set_aggregator::*`      → `::sketches::set_aggregator::*`
- `asap_sketchlib::asap::delta_set_aggregator::*`→ `::sketches::delta_set_aggregator::*`
- `asap_sketchlib::asap::config::*`              → `::asap_runtime::*`

Naming-conflict renames carried through to the consumers:
- `HllDelta` → `HllSketchDelta` (octo_delta::HllDelta still wins the short name)
- `HeapItem` → `CmsHeapItem`   (common::input::HeapItem still wins the short name)

main.rs aliases `asap_sketchlib::asap_runtime as config` so the existing
clap derive references (`config::DEFAULT_CMS_IMPL`, `config::configure(...)`)
still work without touching the rest of the bin.

Tests:
- `cargo build --workspace`                                → clean
- `cargo test -p query_engine_rust --lib precompute_operators` → 141 passed, 0 failed

Depends on ProjectASAP/asap_sketchlib#36 (force-pushed `e473ccc`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor: align CountSketchDelta consumer with sketchlib-go wire format

Track the additive `hh_keys` field on `asap_sketchlib::CountSketchDelta`
so the proto delta path constructs the type with all fields filled in.

Sends an empty `hh_keys` for now: the vendored Rust proto bindings in
`asap_otel_proto::sketchlib::v1` haven't been regenerated against the
latest `.proto` (which carries `hh_keys` on the Go side). The TopK
rebuild on the proto-delta path will fire once those bindings sync;
the sketchlib-go-aligned semantics are already in place underneath.

Bumps the asap_sketchlib git dep to `refactor/wire-format-align-go`
(see asap_sketchlib PR #37).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
zzylol added a commit that referenced this pull request May 5, 2026
* refactor: retire sketch-core mirror

* refactor: switch consumer imports to asap_sketchlib::sketches::*

Update PR #73 against the reorganized asap_sketchlib (PR #36): the
runtime sketches no longer live under a dedicated `asap::` module — they
were merged into the existing `src/sketches/` layout (single home per
sketch concept, ASAP-runtime types appended to the file that already
holds the high-throughput in-process variant).

Mechanical path swaps in asap-query-engine:
- `asap_sketchlib::asap::dd_sketch::*`           → `::sketches::ddsketch::*`
- `asap_sketchlib::asap::count_min::*`           → `::sketches::countmin::*`
- `asap_sketchlib::asap::count_sketch::*`        → `::sketches::count::*`
- `asap_sketchlib::asap::hll_sketch::*`          → `::sketches::hll::*`
- `asap_sketchlib::asap::kll::*`                 → `::sketches::kll::*`
- `asap_sketchlib::asap::count_min_with_heap::*` → `::sketches::cms_heap::*`
- `asap_sketchlib::asap::hydra_kll::*`           → `::sketches::hydra_kll::*`
- `asap_sketchlib::asap::set_aggregator::*`      → `::sketches::set_aggregator::*`
- `asap_sketchlib::asap::delta_set_aggregator::*`→ `::sketches::delta_set_aggregator::*`
- `asap_sketchlib::asap::config::*`              → `::asap_runtime::*`

Naming-conflict renames carried through to the consumers:
- `HllDelta` → `HllSketchDelta` (octo_delta::HllDelta still wins the short name)
- `HeapItem` → `CmsHeapItem`   (common::input::HeapItem still wins the short name)

main.rs aliases `asap_sketchlib::asap_runtime as config` so the existing
clap derive references (`config::DEFAULT_CMS_IMPL`, `config::configure(...)`)
still work without touching the rest of the bin.

Tests:
- `cargo build --workspace`                                → clean
- `cargo test -p query_engine_rust --lib precompute_operators` → 141 passed, 0 failed

Depends on ProjectASAP/asap_sketchlib#36 (force-pushed `e473ccc`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor: align CountSketchDelta consumer with sketchlib-go wire format

Track the additive `hh_keys` field on `asap_sketchlib::CountSketchDelta`
so the proto delta path constructs the type with all fields filled in.

Sends an empty `hh_keys` for now: the vendored Rust proto bindings in
`asap_otel_proto::sketchlib::v1` haven't been regenerated against the
latest `.proto` (which carries `hh_keys` on the Go side). The TopK
rebuild on the proto-delta path will fire once those bindings sync;
the sketchlib-go-aligned semantics are already in place underneath.

Bumps the asap_sketchlib git dep to `refactor/wire-format-align-go`
(see asap_sketchlib PR #37).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(asap_sketchlib): bump pin past PR #39 module renames

Three asap_sketchlib modules were renamed in upstream PR #39:
- sketches::countmin → sketches::countminsketch
- sketches::count    → sketches::countsketch
- sketches::cms_heap → sketches::countminsketch_topk

Backend consumers updated. Cargo.toml pin moved from
refactor/wire-format-align-go branch to main (which now also has
hh_keys restoration via PR #42 and DDSketch + KLL byte parity via
#40 + #41).

Unblocks ASAPCollector Phase 3 step 3 (backend consumes
asap-precompute-rs).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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