Skip to content

fix: warm-tier SimpleEngine answers canonical quantile_over_time + rate queries - #102

Merged
zzylol merged 1 commit into
mainfrom
mvp/phase-3.1-warm-tier-null-answer-fix
May 7, 2026
Merged

zzylol merged 1 commit into
mainfrom
mvp/phase-3.1-warm-tier-null-answer-fix

Conversation

@zzylol

@zzylol zzylol commented May 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add DDSketch to compatible_agg_types(Statistic::Quantile) and HLL to compatible_agg_types(Statistic::Cardinality) so capability matching resolves runtime sketch-emitted aggs that the planner's canonical map (map_statistic_to_precompute_operator) doesn't emit. DDSketch / HLL are wired in via the modified-OTLP path from the agent processors and their query_statistic impls already cover the corresponding statistics — this PR just enumerates them so the matcher's filter doesn't reject the configs.
  • The MVP demo's quantile_over_time(0.99, http_requests_total_latency_ms[1m]) was hitting this gap: the controller plans http_requests_total_latency_ms as a DDSketch directly from mvp-workload.yaml, but when the inference YAML's exact-string find_query_config match doesn't cover the live (phi, range) pair, the engine falls into capability matching and pre-fix returned NoneEngineError::CapabilityMiss → 404. Same pattern unblocks count(http_requests_total{...}) against an HLL-only deploy.

Root cause

asap_types::capability_matching::compatible_agg_types(Statistic::Quantile) listed only [DatasketchesKLL, HydraKLL] — the planner's canonical-pick types. The five-sketch comment at the top of capability_matching.rs notes that HLL / DDSketch / CountSketch route via modified-OTLP and aren't in the canonical map, but the runtime accumulator surface still resolves them, so they need to appear in the compat list. KLL membership was tested; DDSketch / HLL membership wasn't, so the gap shipped.

Fix shape

Option (a) per the task plan: extend compatible_agg_types to enumerate the runtime sketch types whose accumulators answer the statistic natively. No router-level fall-through-to-archive auto-retry.

Test plan

  • cargo test --release -p asap_types --lib (pre-fix flake avg_finds_sum_and_count unchanged — HashMap iteration ordering, not in our blast radius)
  • cargo test --release -p query_engine_rust --test inference_yaml_pattern_coverage — 11 passed including the new canonical_mvp_demo_quantile_over_time_resolves_via_capability_matching
  • cargo build --release — clean
  • cargo test --release -p query_engine_rust --lib — 879/33 (same as origin/main baseline; the 33 pre-existing failures are unchanged)

Files

  • asap-common/dependencies/rs/asap_types/src/capability_matching.rs (+87/-3): compat list expansion + unit regression test + extended five-sketch agreement test
  • asap-query-engine/tests/inference_yaml_pattern_coverage.rs (+57): integration regression through SimpleEngine::handle_query_promql

🤖 Generated with Claude Code

…te queries

Add `DDSketch` to `compatible_agg_types(Statistic::Quantile)` and
`HLL` to `compatible_agg_types(Statistic::Cardinality)` so capability
matching resolves runtime sketch-emitted aggs that the planner's
canonical map (`map_statistic_to_precompute_operator`) does not
emit. The MVP demo's controller plans `http_requests_total_latency_ms`
as a `DDSketch` directly from `mvp-workload.yaml` and routes the
delta payloads through the modified-OTLP wire format; pre-fix,
`quantile_over_time(0.99, http_requests_total_latency_ms[1m])` would
miss capability matching whenever the inference YAML's exact-string
match didn't cover the live phi/range pair, and the warm tier
returned `EngineError::CapabilityMiss`. The runtime accumulator
surface (`DDSketchAccumulator::query_statistic`,
`HllSketchAccumulator::query_statistic`) already supports these
statistics — this PR just enumerates the types that match.

Two regression tests pin the fix:
- `asap_types::capability_matching::ddsketch_resolves_quantile_query_post_fix`
  unit-test for the matcher.
- `query_engine_rust::tests/inference_yaml_pattern_coverage::canonical_mvp_demo_quantile_over_time_resolves_via_capability_matching`
  end-to-end through `SimpleEngine::handle_query_promql` with a
  DDSketch agg whose `query_config` deliberately mismatches the
  live request — forces the capability-matching branch.

`five_sketch_canonical_statistics_in_compat_list` extended to assert
the new DDSketch / HLL membership inline so future divergence fails
the build.

Pre-existing test failures (lib: 33 / asap_types: 1 flaky
`avg_finds_sum_and_count` HashMap-iteration-order test / integration:
`kll_envelope_round_trip_through_backend_adapter`) unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@zzylol
zzylol merged commit 7a14b46 into main May 7, 2026
zzylol added a commit that referenced this pull request May 8, 2026
…hapes (#108)

Adds three regression tests in
`asap-query-engine/src/tests/datafusion/warm_engine_replay_regression_tests.rs`
that pin the warm-tier `SimpleEngine` matcher + capability dispatch for
the two query shapes ProjectASAP/ASAPCollector#46 reported as
500/500 + 500/1000 `status=error` after the PR #333 demo run:

* `quantile_over_time(0.99, http_requests_total_latency_ms[1m])`
  against a `DDSketchAccumulator` — exercises `OnlyTemporal`
  pattern + `Statistic::Quantile` + capability matching now that
  PR #102 added `DDSketch` to `compatible_agg_types(Quantile)`.
* `quantile_over_time(0.5, ...)` variant of the above so the fix
  is not implicitly hard-coded to the 0.99 phi.
* `sum by (zone) (http_requests_total)` instant — exercises the
  `OnlySpatial` pattern + `Statistic::Sum` against a
  `SumAccumulator` window.

Tests use a windowed `(timestamp - 60s, timestamp)` insert (via an
inline `build_engine_with_window` helper) so the store's
`[query_start, query_end)` overlap filter selects them — the
`create_engine_single_pop` factory's zero-duration
`(timestamp, timestamp)` window misses that filter and produces a
spurious `No precomputed outputs found` for `OnlySpatial` queries
whose range is `[end - scrape*1000, end)`.

Audit conclusion: SimpleEngine's `controller_patterns`
(`engines/simple/engine.rs:213-343`) cover both query shapes
verbatim, and capability matching in
`asap-common/dependencies/rs/asap_types/src/capability_matching.rs:160-164`
already lists `DDSketch` for `Statistic::Quantile` (PR #102). No
matcher additions or fixes were needed; the demo's 500/500 +
500/1000 failures are downstream of the matcher path (likely
data-flow: the agent's DDSketch processor renames
`http_requests_total_latency_ms` → `_latency_ms_quantile` before
backend ingest, so the unsuffixed query name finds no data; and
the warm tier ingests counters as `IncreaseAccumulator`, which
does not support `Statistic::Sum` for the bare-counter spatial
sum). Those data-flow gaps are out of scope for this PR but the
regression tests here pin the matcher contract so a future
refactor that breaks the working pattern paths surfaces in
unit-test rather than 25-min-demo time.

Refs: ProjectASAP/ASAPCollector#46

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
zzylol added a commit that referenced this pull request May 8, 2026
…PromQL queries

The agent's DDSketch processor renames raw input metrics
(`http_latency_ms`) to a sketched-form wire name
(`http_latency_ms_quantile`) before emitting to the warm tier — by
design, so warm-tier routing distinguishes raw from sketched form.
But the replay client / PromQL caller still references the
conceptual unsuffixed metric in `quantile_over_time(q, X[range])`,
so the warm engine sees a query for `X` while its sketch store
only holds `X_quantile`. Result: the lookup misses and the engine
returns `status=error` to a query that is logically answerable
(matchers + capability dispatch already work given the right name,
per PR #108).

This change picks Option 1 from the design alternatives — backend
self-heals stale routing tables — because it requires no controller
change and keeps the wire-side rename convention intact:

* New helper `resolve_quantile_metric_alias` in `SimpleEngine`
  parses the query, classifies its shape, and rewrites
  `<metric>` → `<metric>_quantile` only when (a) shape is
  `Quantile` (only shapes whose data lives behind the rename),
  (b) the bare metric is NOT registered locally but the suffixed
  variant IS, and (c) the rewrite re-parses cleanly. Deployments
  without the rename pass through unchanged.
* `handle_query_promql` calls the resolver once at entry so every
  downstream stage (pattern match, `QueryConfig` lookup, capability
  matching, `StoreQueryParams.metric`, schema label lookup) sees
  the same name.
* `replace_metric_token` does identifier-aware substitution
  ([A-Za-z0-9_:] boundary check) so a hypothetical
  `http_latency_ms_total` elsewhere in the query is left alone.

Three new regression tests in `warm_engine_replay_regression_tests`
pin the contract:

1. `quantile_over_time_resolves_unsuffixed_metric_to_quantile_state` —
   the production failure case from ProjectASAP/ASAPCollector#46:
   query `quantile_over_time(0.99, http_latency_ms[1m])` against a
   store that only holds `http_latency_ms_quantile` returns the
   DDSketch quantile, not `status=error`.
2. `non_quantile_query_does_not_rewrite_metric` — `sum_over_time`
   against the same store does NOT rewrite (shape gating).
3. `quantile_query_without_ingest_rename_passes_through` — when the
   bare metric IS registered locally (deployment without rename),
   the resolver leaves the query untouched.

Refs: ProjectASAP/ASAPCollector#46
Builds on: PR #102 (DDSketch in `compatible_agg_types(Quantile)`),
PR #108 (warm-tier matcher coverage tests).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
zzylol added a commit that referenced this pull request May 8, 2026
…PromQL queries (#110)

The agent's DDSketch processor renames raw input metrics
(`http_latency_ms`) to a sketched-form wire name
(`http_latency_ms_quantile`) before emitting to the warm tier — by
design, so warm-tier routing distinguishes raw from sketched form.
But the replay client / PromQL caller still references the
conceptual unsuffixed metric in `quantile_over_time(q, X[range])`,
so the warm engine sees a query for `X` while its sketch store
only holds `X_quantile`. Result: the lookup misses and the engine
returns `status=error` to a query that is logically answerable
(matchers + capability dispatch already work given the right name,
per PR #108).

This change picks Option 1 from the design alternatives — backend
self-heals stale routing tables — because it requires no controller
change and keeps the wire-side rename convention intact:

* New helper `resolve_quantile_metric_alias` in `SimpleEngine`
  parses the query, classifies its shape, and rewrites
  `<metric>` → `<metric>_quantile` only when (a) shape is
  `Quantile` (only shapes whose data lives behind the rename),
  (b) the bare metric is NOT registered locally but the suffixed
  variant IS, and (c) the rewrite re-parses cleanly. Deployments
  without the rename pass through unchanged.
* `handle_query_promql` calls the resolver once at entry so every
  downstream stage (pattern match, `QueryConfig` lookup, capability
  matching, `StoreQueryParams.metric`, schema label lookup) sees
  the same name.
* `replace_metric_token` does identifier-aware substitution
  ([A-Za-z0-9_:] boundary check) so a hypothetical
  `http_latency_ms_total` elsewhere in the query is left alone.

Three new regression tests in `warm_engine_replay_regression_tests`
pin the contract:

1. `quantile_over_time_resolves_unsuffixed_metric_to_quantile_state` —
   the production failure case from ProjectASAP/ASAPCollector#46:
   query `quantile_over_time(0.99, http_latency_ms[1m])` against a
   store that only holds `http_latency_ms_quantile` returns the
   DDSketch quantile, not `status=error`.
2. `non_quantile_query_does_not_rewrite_metric` — `sum_over_time`
   against the same store does NOT rewrite (shape gating).
3. `quantile_query_without_ingest_rename_passes_through` — when the
   bare metric IS registered locally (deployment without rename),
   the resolver leaves the query untouched.

Refs: ProjectASAP/ASAPCollector#46
Builds on: PR #102 (DDSketch in `compatible_agg_types(Quantile)`),
PR #108 (warm-tier matcher coverage tests).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@zzylol
zzylol deleted the mvp/phase-3.1-warm-tier-null-answer-fix branch May 9, 2026 18:00
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