controller: emit 5-sketch routing-connector pipeline (DDSketch/KLL/HLL/CountSketch/CountMinSketch) - #340
Merged
Conversation
…L/CountSketch/CountMinSketch) Rewrites the typed L5 edge YAML emitter so the agent loads all 5 sketch processors and the OTel `routing` *connector* (NOT the deprecated routing processor) dispatches per metric to the right sketch family — the canonical wire shape for MVP issue #46. ## Bugfix call-out: routing is a CONNECTOR, not a processor OTel collector v0.106+ removed `routingprocessor` and re-shipped the routing component as a connector under the top-level `connectors:` block. The legacy emit path placed `routing` under `processors:`, which fails confmap validation at agent boot: `error decoding 'processors': unknown type: "routing"`. This PR emits the connector-form layout the asap-otel binary's builder-config registers: receivers: { otlp } processors: { gorillas3?, batch, ddsketchprocessor, kllprocessor, hllprocessor, countsketchprocessor, countminsketchprocessor } connectors: { routing: { default_pipelines: [...], table: [...route() statements...] } } exporters: { otlp/backend, otlphttp/prometheus? } service.pipelines: metrics: (entry — receivers: [otlp], exporters: [routing]) metrics/raw_passthrough (default — receivers: [routing], processors: [gorillas3?, batch], exporters: [otlp/backend]) metrics/ddsketch_path metrics/kll_path metrics/hll_path metrics/countsketch_path (per-family — receivers: [routing], metrics/countminsketch_path processors: [gorillas3?, <family>processor, batch], exporters: [otlp/backend]) `gorillas3` runs FIRST in every per-sketch pipeline (when an archive tier is declared) so the cold-tier write happens BEFORE the family processor mutates / suffix-renames the stream — same invariant the legacy emit path enforces. ## EdgeStageConfig field Adds `metric_to_family: HashMap<String, SketchKind>` to `EdgeStageConfig` (agreed convention with the parallel planner agent). Empty map ⇒ legacy single-pipeline / Mode-3 / warm-passthrough emit shapes are preserved verbatim (backward-compat). When non-empty, the new `emit_edge_yaml_5sketch_routing` helper emits the canonical 6-pipeline layout. Metrics in the map dispatch per-family; metrics absent fall through to `metrics/raw_passthrough` (this is the home for `http_requests_total` and the freshness probes from PR #333's warm-passthrough routing). ## Field plumbing Updates every `EdgeStageConfig` constructor in `controller/src/` to initialise `metric_to_family: HashMap::new()` so existing callers keep producing the legacy shape. Adds `connectors: HashMap<String, Value>` to the structural `CollectorYaml` type with `skip_serializing_if = "is_empty"` so the legacy single-pipeline shape doesn't gain an empty `connectors: {}` block. ## Test coverage 11 new tests pin the new wire shape: * mvp46_emit_loads_all_5_sketch_processors * mvp46_routing_lives_in_connectors_not_processors (the bugfix) * mvp46_emits_all_6_named_pipelines * mvp46_entry_pipeline_routes_to_connector_not_processor * mvp46_per_sketch_pipelines_use_routing_as_receiver * mvp46_per_sketch_pipelines_have_gorillas3_first_when_archive_declared * mvp46_routing_table_dispatches_per_metric_to_correct_family * mvp46_default_pipeline_is_raw_passthrough * mvp46_warm_passthrough_routes_to_raw_passthrough_pipeline * mvp46_empty_metric_to_family_falls_back_to_legacy_emit * mvp46_composes_with_prometheus_archive_mode3 All 99 `config::*` tests pass; the 10 pre-existing failures on `main` (analyzer, intent_algebra, planner cost_model, opamp, api_tests) are untouched by this change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
zzylol
added a commit
that referenced
this pull request
May 8, 2026
Activates the typed-stage-split path for every compose invocation that includes base.yml, so the demo (and any ad-hoc `docker compose up controller` against base alone) routes through the typed pipeline: bootstrap GET (#329), OpAMP-on-connect replan (#334), and per-metric 5-sketch family routing (#339 + #340 + the in-flight stitching PR). The mvp-multi-stage.yml overlay already sets the same default; this just promotes the gate to the base layer so it is on without the overlay. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2 tasks
zzylol
added a commit
that referenced
this pull request
May 8, 2026
…_family map (#46) (#342) PR #339 (planner: per-metric SketchExpr) and PR #340 (emitter: metric_to_family-driven 5-sketch routing) landed in parallel, but nothing populated `EdgeStageConfig.metric_to_family`, so the new routing-connector wire path stayed dormant — every bootstrap and OpAMP-push emitted single-pipeline DDSketch even with the six contract metrics declared in `workloads.yaml`. This stitches them: a new `config::collect_metric_to_family` walks the workload registry, runs `bind_workload_typed` per metric, and collects committed sketch families into the HashMap. Metrics that decline binding (`http_requests_total` raw passthrough, exact-required, multi-intent) are skipped — the emitter routes those to `metrics/raw_passthrough` by default. Wired into both: * `main::emit_bootstrap_typed` — also generalised the candidate resolution so a registry whose first agent-role entry is raw (e.g. `http_requests_total`) walks to the next binding metric for the edge_cfg shape rather than falling back to legacy. * `replan::Replanner::try_emit_typed_edge_yaml_for_workload` — same stitch so OpAMP-pushed YAML on reconnect / replan also activates the routing connector. Acceptance test (`api_tests::bootstrap_emits_5sketch_routing_for_six_contract_metrics`): loads all six MVP §46 contract metrics into the registry, hits the bootstrap GET endpoint with `USE_TYPED_STAGE_SPLIT=1`, and asserts the emitted YAML contains all 5 sketch processors, `routing` under `connectors:` (not `processors:`), all 6 named pipelines, and a `route() where metric.name == "..."` rule for each sketched metric. 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
Rewrites the typed L5 edge YAML emitter (
controller/src/config/stage_config.rs::emit_edge_yaml) so the agent loads all 5 sketch processors and the OTelroutingconnector (NOT the deprecated routing processor) dispatches per metric to the right sketch family — the canonical wire shape for issue #46.Bugfix call-out:
routingis a CONNECTOR, not a processorOTel collector v0.106+ removed
routingprocessorand re-shipped the routing component as a connector under a top-levelconnectors:block. The legacy emit path placedroutingunderprocessors:, which failsconfmapvalidation at agent boot:The asap-otel binary's
builder-config.yamlregistersroutingconnector. This PR emits the connector-form layout that build expects.Canonical YAML shape
The 6 named pipelines
metrics— entry (receiverotlp, exporterrouting)metrics/raw_passthrough— default fall-through (e.g.http_requests_total, freshness probes)metrics/ddsketch_pathmetrics/kll_pathmetrics/hll_pathmetrics/countsketch_pathmetrics/countminsketch_path(Plus
metrics/prometheus_archivewhen Mode-3 metrics are also configured.)EdgeStageConfig field
Adds
metric_to_family: HashMap<String, SketchKind>toEdgeStageConfig(agreed convention with the parallel planner agent). Empty map ⇒ legacy single-pipeline / Mode-3 / warm-passthrough emit shapes preserved verbatim.gorillas3 invariant
gorillas3runs FIRST in every per-sketch pipeline so the cold-tier write happens BEFORE the family processor mutates / suffix-renames the stream — same invariant the legacy emit path enforces, pinned bymvp46_per_sketch_pipelines_have_gorillas3_first_when_archive_declared.Freshness-probe routing (PR #333)
warm_passthrough_metricsroute tometrics/raw_passthroughso the metric name is preserved end-to-end (no DDSketch_quantilesuffix). Pinned bymvp46_warm_passthrough_routes_to_raw_passthrough_pipeline.Test plan
config::*tests passmvp46_emit_loads_all_5_sketch_processorsmvp46_routing_lives_in_connectors_not_processors(the bugfix)mvp46_emits_all_6_named_pipelinesmvp46_entry_pipeline_routes_to_connector_not_processormvp46_per_sketch_pipelines_use_routing_as_receivermvp46_per_sketch_pipelines_have_gorillas3_first_when_archive_declaredmvp46_routing_table_dispatches_per_metric_to_correct_familymvp46_default_pipeline_is_raw_passthroughmvp46_warm_passthrough_routes_to_raw_passthrough_pipelinemvp46_empty_metric_to_family_falls_back_to_legacy_emitmvp46_composes_with_prometheus_archive_mode3main(analyzer / intent_algebra / planner cost_model / opamp / api_tests — 10 total) are untouchedRefs: #46
🤖 Generated with Claude Code