feat(controller): implement control plane in Rust (phases 1–4) + e2e test - #61
Merged
Merged
Conversation
Closes #36 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 1 – Static planner - internal/types: shared domain types (QueryWorkload, CollectionPlan, etc.) - internal/analyzer: QuerySpec → QueryWorkload parser with validation - internal/planner/rules: deterministic sketch selection and window strategy - internal/config: OTel collector YAML generation (agent + backend) - internal/opamp: WebSocket OpAMP server (push RemoteConfig, receive AgentStatus) - internal/store: thread-safe plan store with rollback and expiry - cmd/controller: HTTP API wired to analyzer → planner → OpAMP push Phase 2 – Cost model - internal/planner/cost_model: benchmark-driven scoring (bandwidth, CPU, memory, accuracy); CostModelPlanner picks Pareto-optimal sketch per SLA Phase 3 – Feedback loop - internal/monitor/feedback: scrapes collector /metrics, detects bandwidth / accuracy / CPU violations, fires OnViolationFunc for re-planning Phase 4 – ASAPQuery precompute integration - internal/config/precompute_config: ShouldPrecompute scheduling rule, BuildPrecomputeJobs, PrecomputeClient (Register / Deregister HTTP client) - controller.proto: full proto definition for all RPCs and types 57 unit tests, all passing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the Go implementation with idiomatic Rust per the design doc. Phase 1 – Static planner - src/types.rs: domain types (QueryWorkload, CollectionPlan, SketchType, etc.) - src/analyzer.rs: QuerySpec → QueryWorkload with validation + duration parser - src/planner/rules.rs: deterministic sketch selection, window/batch strategy - src/config/agent.rs: OTel collector YAML generation (serde_yaml) - src/config/backend.rs: backend merge-collector YAML generation - src/opamp/mod.rs: WebSocket OpAMP server (axum ws, tokio mpsc channels) - src/store/mod.rs: thread-safe plan store with rollback + expiry - src/main.rs: axum HTTP API + OpAMP server wired together Phase 2 – Cost model - src/planner/cost_model.rs: benchmark-driven scoring (bandwidth, CPU, memory, accuracy); CostModelPlanner picks Pareto-optimal sketch per SLA Phase 3 – Feedback loop - src/monitor/mod.rs: Prometheus text scraper, delta-based CPU detection, three violation kinds (Bandwidth, Accuracy, Cpu), pluggable OnViolationFn Phase 4 – ASAPQuery precompute integration - src/config/precompute.rs: ShouldPrecompute scheduling rule, BuildPrecomputeJobs, async PrecomputeClient (reqwest, register/deregister) Dependencies: tokio, axum (ws), serde_yaml, reqwest, anyhow, thiserror, chrono 68 unit tests, all passing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix generate_agent_config() to emit a complete, valid OTel collector YAML: adds receivers (OTLP gRPC/HTTP) and exporters (Prometheus :8889) sections, and includes receivers/exporters in service.pipelines.metrics. - Remove opamp extension from generated config: the opamp-go client expects binary protobuf but the controller speaks JSON, causing repeated "bad handshake" errors. Config delivery uses the HTTP config provider (GET /api/v1/config/:metric) instead. - Remove drop_original from generated YAML; it is not a real ddsketch processor config key. - Add GET /api/v1/config/:metric endpoint: returns the full collector YAML for a planned metric, enabling --config=http://... usage. - Add sketch_type_override to QuerySpec and QueryWorkload: allows callers to pin a specific sketch type, bypassing the cost-model planner. Needed for collectors that support only a subset of sketches (e.g. ddsketchcol only has ddsketch). - Add docs/e2e-test-guide.md: human-readable 7-step walkthrough. - Add tests/otel_controller_e2e_test.sh: automated end-to-end test (controller + ddsketchcol + e2esdkbench); includes port-cleanup guard to prevent failures from stale processes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
Author
SieDeta
pushed a commit
that referenced
this pull request
Apr 17, 2026
…he-control-plane feat(controller): implement control plane in Rust (phases 1–4) + e2e test
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
Implements the control plane described in
docs/control-plane-design.mdacross all four phases, in Rust, verified end-to-end against the custom OTel collector and SDK load generator.Design (from
docs/control-plane-design.md)The controller is the "brain" that observes the query workload and drives configuration across the entire pipeline — from agent OTel collectors to SimpleStore precomputation — to minimize bandwidth and latency while meeting accuracy SLAs.
Four coupled decisions per metric × workload:
aggregate_byset to satisfy group-by + filter requirementswindowmode whenLatencySLA ≥ TimeWindow,batchotherwisePlanning algorithm:
Config delivery: The opamp extension is omitted from generated configs because
opamp-goexpects binary protobuf while the controller speaks JSON, causing handshake errors. Configs are delivered via the OTel HTTP config provider (--config=http://controller:8080/api/v1/config/<metric>) instead.Implementation
Phase 1 — Static planner
types.rs: domain types (QueryWorkload,CollectionPlan,AgentCollectorConfig,SketchType, etc.)analyzer.rs:QuerySpec→QueryWorkloadwith duration parsing and label deduplicationplanner/rules.rs: deterministic sketch selection (Quantile→DDSketch, Cardinality→HLL, Frequency→CountSketch)config/agent.rs: full OTel collector YAML generation (receivers, processors, exporters, service pipeline)config/backend.rs: backend merge-processor YAML generationstore/mod.rs: plan store with rollback and TTL expiryopamp/mod.rs: OpAMP WebSocket server (agent registry, push, push_all)Phase 2 — Cost model
planner/cost_model.rs: benchmark-driven Pareto selection (lowest bandwidth meeting AccuracySLA);ValidUntilTTLQuerySpec.sketch_typeoverride: pin a specific sketch type, bypassing the cost model (useful when a collector supports only a subset)Phase 3 — Feedback loop
monitor/mod.rs: async Prometheus scraper; delta-based CPU, bandwidth, and error-rate violation detection;OnViolationFncallbackPhase 4 — Precompute integration
config/precompute.rs: eligibility check (repeat_every < latency_sla), job builder,PrecomputeClient(register/deregister via reqwest)HTTP config provider endpoint
GET /api/v1/config/:metric— serves the full collector YAML for any planned metric, enabling--config=http://controller:8080/api/v1/config/<metric>without requiring OpAMP protobufTest plan
cargo test)tests/otel_controller_e2e_test.shddsketchcolvia HTTP config provider → runse2esdkbench(500 series × 25k data-points/s × 30 s) → verifies collector processed datadocs/e2e-test-guide.md🤖 Generated with Claude Code