feat(e2e): all-five-sketch runtime path + harness — controller, processor, fake-exporter, P1–P9 - #204
Merged
Merged
Conversation
…ssor, fake-exporter, P1–P9
Lands the real e2e: PromQL → controller → agent (sketchcol) → backend
(precompute_engine) → PromQL response, through the modified-OTLP wire
format (typed Metric.data = {DDSketch | KLLSketch | HLLSketch |
CountSketch | CountMinSketch}). One soak per sketch verified against
its accuracy envelope; P1–P9 harness drives the full sweep matrix.
Controller / data sink:
- AgentDataSink enum (Otlp{endpoint,...} vs PrometheusScrape{...})
in types.rs; agent.rs picks the exporter at config-gen time
instead of always emitting `prometheus`.
- OpAMP: strip varint header from incoming WS frames before proto
decode; outbound ServerToAgent carries ReportFullState +
Accept/Offer capability bitmask so agents accept and apply config.
Sketchcollector / processor:
- ddsketchprocessor: replace DataDog sketches-go with sketchlib-go
DDSketch so the proto envelope is decodable by asap_sketchlib's
DDSketchState.
- builder-config-sketches.yaml (renamed from builder-config-ddonly):
compiles all five sketch processors plus opampextension under
OCB v0.141.0.
Fake-exporter:
- swappable_filter.go (P2): atomic.Pointer-backed Stream.AttributeFilter
wired through `POST /control/projection`; SDK needed no patch.
- raw_tee.go (P4): hour-bucketed JSONL writer matching the Rust
RawSample wire format byte-for-byte; mounted at EXPORTER_RAW_TEE_ROOT.
- main.go: wired into runSynthetic + runTraceReplay.
Harness (P5–P9):
- promql_replay.py: PromQL fan-out at fixed QPS, plan-id-tagged JSONL.
- plan_transition.py: t_query_in / t_plan_ready / t_first_hit /
t_steady against the controller plan-id stream + 1 Hz docker stats.
- run_e2e_sweep.sh: {DDSketch,KLL,CS,CMS,HLL} × {N=1,10} × {scrape=
100ms,1s} × {card=1e3,1e4,1e5} = 60 cells.
- accuracy_reduce.py: cold-truth ⋈ replay → relative error per row.
- e2e_plots.py: pareto / bandwidth / transition / latency CDF.
Configs / overlays:
- New per-sketch overlays (e2e-overlay-{cms,cs,hll,kll}.yml + base
e2e-overlay.yml) and per-sketch agent + backend configs
(sketchcol-agent-*-direct.yaml, backend-streaming-*.yaml,
backend-inference-*.yaml).
Known limitation: stock OTel gateway 0.108 can't translate
DDSketch/HLLSketch through prometheusremotewrite, so warm-tier
sketch ingest is dropped at the gateway — the cold-tier path
(P1+P4) carries the e2e flow through. Fix is to enable OTLP ingest
on precompute_engine or swap the backend image to query_engine_rust.
Tracked in PROGRESS.md follow-ups.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merged
8 tasks
zzylol
added a commit
to ProjectASAP/ASAPQuery-backend
that referenced
this pull request
May 1, 2026
Lands the backend half of the runtime e2e: each sketch type now has
a verified PromQL → backend path through the modified-OTLP wire
format (typed Metric.data = {DDSketch | KLLSketch | HLLSketch |
CountSketch | CountMinSketch}).
query_statistic implementations:
- DDSketchAccumulator: Quantile / Sum / Count / Min / Max.
- HllSketchAccumulator: Cardinality, with Count accepted as a
Cardinality alias for the existing PromQL count(...) path.
- CountSketchAccumulator: Topk / Count / Sum, no-key fallback
returns row-mean total.
- CountMinSketchAccumulator: Count / Sum, no-key fallback returns
the min-row sum — canonical CMS total-event estimator that's
exact when each insert increments one cell per row (and CMS
never under-counts).
accumulator_factory.rs: DDSketchAccumulatorUpdater wired in
alongside CMS / CountSketch / KLL / HLL updaters so the
precompute_engine recognises AggregationType::DDSketch from
streaming.yaml.
Modified-OTLP envelope decoders go through
from_sketchlib_proto_bytes / from_msgpack_bytes against
SketchEnvelope { sketch_state: Some(SketchState::*(state)) }.
P1 — cold-store flag wiring (asap-query-engine/main.rs):
- --cold-store-root with env = "ASAP_COLD_STORE_ROOT" plumbed
through a build_adapter_config helper that selects
prometheus_promql_with_cold when set.
- Combine with --forward-unsupported-queries to keep Prom as the
tail of the chain.
- Four unit tests pin the wiring matrix (cold × forward).
Companion PR in ASAPCollector lands the agent + harness
(see ProjectASAP/ASAPCollector#204).
Known cleanup (not a blocker): compatible_agg_types in
capability_matching.rs doesn't list CountMinSketch under
Statistic::Sum, but query_logics treats CMS as the canonical
approximator for both Sum and Count. The exact-match
find_query_config path bypasses capability matching, so the
runtime e2e succeeds today; reconciling the two tables is a
separate change.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 1, 2026
Merged
zzylol
added a commit
that referenced
this pull request
May 6, 2026
…pr + Schema flow) (#275) Per design.md §6 "core::intent_algebra — Layer 3", introduce the L3 intent IR the planner pivots on, in a new `controller/src/intent_algebra/` module. Phase A (#273) shipped the typed QuerySpec types; this PR ships the algebra + schema flow they describe a workload over. What lands: - `AggIntent` — vocabulary of "what to compute, not how": Count, Sum, Min, Max, Avg, Quantile{q, accuracy}, TopK{k, accuracy}, Cardinality{accuracy}, Frequency{accuracy}, Rate{window}, Increase{window}. No sketch types here — sketch binding is L4. - `QueryExpr` — the L3 algebra DAG, single-rooted per query: Scan, Window, Aggregate, LetBinding, Ref. Variant subset chosen for the DC + PromQL deployment scope per the orchestrator spec; Filter/Project/ Partition/Distinct/Merge/Join/SetOp/Sort/Limit/Subquery/WindowFunc/ BinaryOp are deferred so each lands with a planner consumer rather than as dead code (additive growth). - `Schema` — typed schema flowing on every L3 edge with `unique_keys` populated per design.md §6 schema-flow table. `unique_keys` is the load-bearing CSE-legality field (design.md §6 line ~1284 + the batched-queries example); `cse_substitution_legal_only_with_unique_keys` pins this invariant as a unit test. - `lower_parsed_query(parsed, accuracy) → QueryExpr` — single-query lowering from the existing `query_parser::ParsedQuery`. Workload- level CSE that produces fan-in (multi-root with LetBinding/Ref) is the follow-up's job. Per-variant schema propagation rules match the design.md table: Scan emits the source schema with unique_keys from its catalog; Window propagates row identity (carries unique_keys verbatim, requires time_index on input); Aggregate{by, ..} emits unique_keys = [by] and strips the time axis; LetBinding/Ref propagate the bound expr's schema. Wire-up state. Nothing in `analyzer::Analyzer` or `planner/` consumes these types yet — that's the follow-up Phase C PR. Phase B exposes the IR so that wiring becomes a focused change rather than co-emission of new types + new consumers. Tests added (in module): agg_intent_serde_roundtrip, output_column_names_are_intent_keyed, quantile_output_is_float64, sum_preserves_input_dtype, schema_serde_roundtrip, schema_new_*, schema_with_time_index_*, add_unique_key_dedupes, query_expr_simple_aggregate, query_expr_let_binding_ref, query_expr_unresolved_ref_errors, query_expr_window_requires_time_index, query_expr_aggregate_invalid_by_column, query_expr_serde_roundtrip, cse_substitution_legal_only_with_unique_keys, lower_promql_basic, lower_promql_with_group_by, lower_promql_cardinality, lower_empty_metric_errors. design.md §6 grows an "Implementation status" subsection naming the Phase B subset and the deferred phases C/D/E/F. Note on cargo test status. The controller's `cargo test` target was already broken at origin/main (post-#273): nine `data_sink` field-init errors in `src/config/agent.rs`, `src/config/asapquery_backend.rs`, `src/config/precompute.rs`, and `src/main.rs` test code. This is pre-existing breakage from the `data_sink` field added to `AgentCollectorConfig` in #204; intent_algebra introduces no new test errors. `cargo build` and `cargo clippy --bins` are both clean for intent_algebra. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tasks
zzylol
added a commit
that referenced
this pull request
May 6, 2026
…nfig constructors (#276) PR #204 added the `data_sink: AgentDataSink` field to `AgentCollectorConfig` (production sites updated) but missed nine test-fixture / test-code constructors, leaving `cargo test --release -p controller` blocked at compile-time. This broke the baseline for downstream PRs (#273, #274, #275) which could not run their newly added unit tests. Mechanical fill-in only, no semantic change to existing tests: - 7 sites use `AgentDataSink::default()` (Otlp-to-backend) — the canonical default that PR #204 introduced for new pipelines. - 2 sites (`config::agent::tests::ddsketch_cfg` and `main::api_tests::generated_agent_yaml_contains_opamp_extension`) pin `AgentDataSink::PrometheusScrape { endpoint: "0.0.0.0:8889" }` because their pre-existing assertions check for the legacy `prometheus` exporter on :8889. Pinning the sink keeps the test semantics intact rather than rewriting the asserts. After the fix: - `cargo build --release -p controller` clean. - `cargo test --release -p controller --no-run` clean (was the blocker). - `cargo test --release -p controller` runs 395 tests; 389 pass, 6 pre-existing failures unrelated to `data_sink`: * 2 in `analyzer::tests` — float-precision asserts. * 4 in `opamp::tests` / `api_tests` — protobuf framing ("invalid tag value: 0") on `ServerToAgent` decode. These are tracked separately and out of scope for this PR. - Newly-shipped tests now run end-to-end: `query_language` (8), `language_logical_plan` (6), `types_v2` (7), `algebra` (intent_algebra family, 6+). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Lands the real e2e: PromQL → controller → agent (sketchcol) → backend (
precompute_engine) → PromQL response, through the modified-OTLP wire format (typedMetric.data = {DDSketch | KLLSketch | HLLSketch | CountSketch | CountMinSketch}). Each sketch type now has a runtime-verified path with a soak against its accuracy envelope; the P1–P9 harness drives the full sweep matrix.histogram_quantile(0.5/0.9/0.99,…)histogram_quantile(0.5,…)count(http_requests_total)sum_over_time(http_requests_total[1m])sum_over_time(http_requests_total[1m])Cross-cutting fixes
data_sink(types.rs,config/agent.rs):AgentDataSinkenum picks betweenOtlp{endpoint,…}andPrometheusScrape{…}instead of always emittingprometheus.opamp/mod.rs): strip varint header from incoming WS payloads before proto decode; outboundServerToAgentcarriesReportFullState+ Accept/Offer capability bitmask so agents accept and apply config.ddsketchprocessor: replaced DataDogsketches-gowithsketchlib-go/DDSketchso the proto envelope is decodable byasap_sketchlib'sDDSketchState.builder-config-sketches.yaml(renamed frombuilder-config-ddonly.yaml) compiles all five sketch processors plusopampextensionunder OCB v0.141.0.Harness (P1–P9)
deploy/fake-exporter/swappable_filter.go— atomic.Pointer-backedStream.AttributeFilter, swap viaPOST /control/projection. No SDK patch needed.deploy/fake-exporter/raw_tee.go— hour-bucketed JSONL ground-truth writer, byte-for-byte compatible with the RustRawSamplewire format. Wired intorunSynthetic+runTraceReplay.deploy/scripts/promql_replay.py— fixed-QPS PromQL fan-out, plan-id-tagged JSONL.deploy/scripts/plan_transition.py—t_query_in / t_plan_ready / t_first_hit / t_steadyagainst the controller plan-id stream + 1 Hz docker stats.deploy/scripts/run_e2e_sweep.sh—{DDSketch,KLL,CS,CMS,HLL} × {N=1,10} × {scrape=100ms,1s} × {card=1e3,1e4,1e5}(60 cells).deploy/scripts/accuracy_reduce.py— cold-truth ⋈ replay → relative-error CSV.deploy/scripts/e2e_plots.py— pareto / bandwidth / transition / latency-CDF figures.Known limitation
Stock OTel gateway 0.108 can't translate
DDSketch/HLLSketchthroughprometheusremotewrite, so warm-tier sketch ingest is dropped at the gateway — the cold-tier path (P1+P4) carries the e2e flow today. Tracked in PROGRESS.md follow-ups: enable OTLP ingest onprecompute_engine(or swap the backend image toquery_engine_rust, which already has it).Companion PR in ASAPQuery-backend lands the matching
query_statisticimpls +--cold-store-rootwiring.Test plan
docker compose -f base.yml -f agents-N1.yml -f baseline-b3-delta.yml -f e2e-overlay.yml up -dbrings the stack up cleanlyswappable_filterHTTP swap returns{"applied":"zone"}raw_teewrites ground-truth JSONL with the right path layout (8 unit tests incl. concurrent-writer race + format anchor)pareto_acc_vs_thru.png+query_latency_cdf.pngfrom real data🤖 Generated with Claude Code