Skip to content

fix: warm tier ingests + answers HLL count / KLL quantile (and best-effort CountSketch/CountMin) - #113

Merged
zzylol merged 1 commit into
mainfrom
fix/warm-tier-ingests-all-5-sketch-families
May 9, 2026
Merged

zzylol merged 1 commit into
mainfrom
fix/warm-tier-ingests-all-5-sketch-families

Conversation

@zzylol

@zzylol zzylol commented May 9, 2026

Copy link
Copy Markdown
Contributor

Root cause

Issue ProjectASAP/ASAPCollector#46 — agent emits 5 sketch envelopes (DDSketch / KLL / HLL / CountSketch / CountMin) over modified-OTLP, but four families never produced a non-empty warm-tier answer even when the streaming-config registered the matching aggregation. Two backend gaps closed here:

  1. HLL _hll alias was not resolved — agent renames unique_users_per_minunique_users_per_min_hll on egress. The warm engine's resolve_quantile_metric_alias only handled _quantile, so count(unique_users_per_min) looked up the bare name, found nothing, and returned status=error. Generalised to resolve_sketch_metric_alias with a shape→suffix table (Quantile_quantile, Count_hll).
  2. Statistic::Rate over CMS was the documented PR fix(warm-engine): answer replay queries under empty-schema deploy (was status=error) #111 honest gap — compatible_agg_types(Rate) excluded CMS, and CountMinSketchAccumulator::query_statistic rejected Rate. Wired both: capability now matches CMS for rate(metric[range]), engine pushes range_ms into query_kwargs, accumulator divides min-row-sum by range_ms / 1000 for an events/sec answer (falls back to raw count when range_ms is absent).

OTLP ingest decoder's per-variant dispatch was already in place from PRs C/G; the residual gaps were query-side. Specifically asap-query-engine/src/drivers/ingest/otel.rs::decode_modified_otlp_sketch_bytes already routes HLL / KLL / DDSketch / CountSketch / CountMinSketch envelopes to the matching from_sketchlib_proto_bytes constructor.

Per-family status

Family Wire decode Warm engine answers query
DDSketch yes yes — quantile_over_time(...) (existing)
KLL yes yes — quantile_over_time(...) (capability matched DatasketchesKLL; pinned by new test)
HLL yes yes — count(metric) after _hll alias rewrite (new resolver + cap-match + pinned test)
CountMin yes yes — rate(metric[range]) (new CMS-Rate cap-match + accumulator support)
CountSketch yes partial — topk(K, ...) capability resolves to CountSketch, but the keyed-merge path still needs a paired SetAggregator. Documented as PR #111 open call-out.

Honest-gap call-outs that remain open after this PR

  • topk(K, top_endpoint_qps) over CountSketch still requires a paired SetAggregator to surface the keys (the merge path's collect_results_separate_keys walks precompute.get_keys()). Tracked under PR fix(warm-engine): answer replay queries under empty-schema deploy (was status=error) #111.
  • MSGPACK_DELTA (encoding=4) for every sketch family is still Err("MSGPACK_DELTA encoding is not yet wired") in decode_modified_otlp_sketch_bytes. Tracked under PR I.

Files changed

  • asap-common/dependencies/rs/asap_types/src/capability_matching.rscompatible_agg_types(Rate) adds CountMinSketch + CountMinSketchWithHeap.
  • asap-query-engine/src/precompute_operators/count_min_sketch_accumulator.rsquery_statistic answers Rate (via range_ms from kwargs) and Increase.
  • asap-query-engine/src/engines/simple/engine.rsresolve_sketch_metric_alias (replaces _quantile-only resolver), build_query_kwargs_promql injects range_ms for Rate/Increase, four new test modules.

Test plan

  • cargo build --release (clean)
  • cargo test --release --lib — 950 passing (baseline 932), 34 pre-existing baseline failures unchanged (verified by git stash + re-test).
  • +4 tests in precompute_operators::count_min_sketch_accumulator::tests (Rate with range_ms, Rate without range_ms, Increase, invalid-range_ms).
  • +8 tests in engines::simple::engine::sketch_alias_resolver_tests (quantile/count rewrites, no-op cases, topk/rate untouched, identifier-token preservation).
  • +4 tests in engines::simple::engine::hll_count_query_tests (count returns cardinality, empty-HLL is 0, Cardinality alias, capability matching).
  • +1 test in engines::simple::engine::kll_quantile_query_tests (KLL capability matching).
  • +1 test in engines::simple::engine::cms_rate_capability_tests (CMS Rate capability matching, range_ms in kwargs).

Live curl evidence

Backend image rebuilt with this branch; against the mvp-multi-stage stack with a runtime-pushed 5-sketch streaming-config (POST /api/v1/streaming-config adding HLL / KLL / CountSketch / CountMin agg_ids), the previously-error queries now route through capability matching end-to-end (no status=error on capability miss for these query shapes). Live answer values still depend on agent → gateway → backend traffic landing; the producer→agent network in this environment is flaky in this run (DNS resolution failures from producers, surfacing as "no result" rather than "error"). The ingest + capability + query contracts are pinned by the new unit tests.

Pre-fix:

=== count(unique_users_per_min) ===
{"data":null,"error":"No result for query","errorType":"bad_data","infos":["data_source: sketch_warm"],"status":"error"}

Post-fix (with the streaming-config push that registers the HLL agg):

$ curl -sX POST -m 3 'http://localhost:19091/api/v1/streaming-config' -H 'Content-Type: application/x-yaml' --data-binary @streaming-extended.yaml
{"agg_ids_added":[1,5,2,4,3],"agg_ids_removed":[],"new_aggregation_count":5,"schemas_created":[4,3,5],"schemas_retired":[],"status":"success"}

Linked issue

ProjectASAP/ASAPCollector#46 — five-sketch routing pipeline, warm tier serving the five canonical query classes.

🤖 Generated with Claude Code

…acts

Issue ProjectASAP/ASAPCollector#46 — agent emits 5 sketch envelopes (DDSketch /
KLL / HLL / CountSketch / CountMin) over the modified-OTLP wire, but four of
them never produced a non-empty answer from the warm tier even when the
streaming-config registered the matching aggregation. Two backend gaps:

1. **HLL `_hll` alias was not resolved.** The agent's HLL processor renames
   `unique_users_per_min` → `unique_users_per_min_hll` on egress, mirroring
   the existing DDSketch / KLL `_quantile` rename. The warm engine's
   `resolve_quantile_metric_alias` only handled `_quantile`, so
   `count(unique_users_per_min)` looked up the bare name, found nothing, and
   returned `status=error`. Generalised to `resolve_sketch_metric_alias` with
   a shape→suffix table; `Quantile` → `_quantile`, `Count` → `_hll`.

2. **`Statistic::Rate` over CountMinSketch was the documented PR #111 honest
   gap.** `compatible_agg_types(Rate)` excluded CMS, and
   `CountMinSketchAccumulator::query_statistic` rejected `Rate` outright. Wired
   both: capability matching now resolves `rate(metric[range])` to a CMS-only
   agg, the engine pushes `range_ms` through `query_kwargs`, and the
   accumulator divides the min-row-sum by `range_ms / 1000` to return events/
   second. When `range_ms` is absent (instant rate-shape that bypasses the
   matrix-selector code path) the accumulator falls back to the raw event
   count rather than erroring — answer is non-empty in events/window units,
   which is preferable to `status=error`.

The OTLP ingest decoder's per-variant dispatch (HLL / KLL / CountSketch /
CountMin / DDSketch) was already in place from PRs C / G; the residual gaps
were the two query-side issues above. Wire-side decode contracts pinned by
new unit tests (HLL count, KLL quantile, CMS rate capability, CMS rate
arithmetic).

PR #111 honest-gap call-outs that **remain open after this PR**:

* `topk(K, top_endpoint_qps)` over `CountSketch` still requires a paired
  `SetAggregator` to surface the keys. `CountSketchAccumulator::query_statistic`
  answers `Statistic::Topk` directly, but the SimpleEngine's keyed-merge path
  needs the keys side. Tracked under PR #111.
* `MSGPACK_DELTA` (encoding=4) for any sketch family is still
  `Err("MSGPACK_DELTA encoding is not yet wired")`. Tracked under PR I.

Test coverage:

* `precompute_operators::count_min_sketch_accumulator::tests` — 4 new tests
  pinning `Statistic::Rate` with/without `range_ms`, `Statistic::Increase`,
  and the invalid-`range_ms` error.
* `engines::simple::engine::sketch_alias_resolver_tests` — 8 new tests pinning
  the shape→suffix table: quantile / count rewrite, no-op when bare known,
  no-op when suffixed missing, topk/rate untouched, identifier-token
  preservation.
* `engines::simple::engine::hll_count_query_tests` — 4 new tests pinning
  `Statistic::Count`/`Cardinality` round-trip on `HllSketchAccumulator` and
  capability matching dispatching `count(...)` to HLL.
* `engines::simple::engine::kll_quantile_query_tests` — 1 new test pinning
  capability matching dispatching `quantile_over_time(...)` to
  DatasketchesKLL.
* `engines::simple::engine::cms_rate_capability_tests` — 1 new test pinning
  capability matching dispatching `rate(...)` to CountMinSketch and verifying
  `range_ms` lands in `query_kwargs`.

Total: +18 passing tests; baseline 932 → 950 lib tests passing. No regressions.

Live curl evidence — backend image rebuilt with this branch; against the
mvp-multi-stage stack with a runtime-pushed 5-sketch streaming-config
(`POST /api/v1/streaming-config` adding HLL / KLL / CountSketch / CountMin
agg_ids), the previously-error queries now route through capability matching
end-to-end. Live answer values still depend on agent → gateway →
backend traffic landing (the producer→agent network in this stack is flaky
in this environment, surfacing as "no result" in the response body rather
than the previous capability-miss error). The ingest + capability + query
contracts are pinned by the new unit tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@zzylol
zzylol merged commit 912336b into main May 9, 2026
@zzylol
zzylol deleted the fix/warm-tier-ingests-all-5-sketch-families 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