Skip to content

feat(controller): implement control plane in Rust (phases 1–4) + e2e test - #61

Merged
zzylol merged 5 commits into
mainfrom
36-discussion-how-to-design-the-control-plane
Mar 20, 2026
Merged

zzylol merged 5 commits into
mainfrom
36-discussion-how-to-design-the-control-plane

Conversation

@zzylol

@zzylol zzylol commented Mar 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Implements the control plane described in docs/control-plane-design.md across 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:

  1. Sketch vs. raw — based on query type, bandwidth budget, and latency SLA
  2. Sketch type — Quantile → DDSketch/KLL, Cardinality → HLL, Frequency → CountSketch/CountMinSketch
  3. Dimension aggregation — minimal aggregate_by set to satisfy group-by + filter requirements
  4. Time window strategy — window mode when LatencySLA ≥ TimeWindow, batch otherwise

Planning algorithm:

1. Classify aggregation types → candidate sketches
2. Determine minimum label dimensions (GroupByLabels ∪ LabelFilter keys)
3. Score candidates by bandwidth/CPU/accuracy using benchmark table → pick lowest bandwidth meeting AccuracySLA
4. Select window strategy (window vs. batch)
5. Build CollectionPlan → push via HTTP config provider → register precompute jobs
6. Monitor: if actual_accuracy < AccuracySLA → re-plan

Config delivery: The opamp extension is omitted from generated configs because opamp-go expects 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 → QueryWorkload with duration parsing and label deduplication
  • planner/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 generation
  • store/mod.rs: plan store with rollback and TTL expiry
  • opamp/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); ValidUntil TTL
  • QuerySpec.sketch_type override: 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; OnViolationFn callback

Phase 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 protobuf

Test plan

  • 71 unit tests across all modules (cargo test)
  • End-to-end test script: tests/otel_controller_e2e_test.sh
    • Starts controller → submits plan → fetches YAML config → starts ddsketchcol via HTTP config provider → runs e2esdkbench (500 series × 25k data-points/s × 30 s) → verifies collector processed data
  • Human-readable walkthrough: docs/e2e-test-guide.md
bash tests/otel_controller_e2e_test.sh           # full run (builds both binaries)
bash tests/otel_controller_e2e_test.sh --skip-build  # fast re-run

🤖 Generated with Claude Code

Closes #36

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@zzylol zzylol linked an issue Mar 20, 2026 that may be closed by this pull request
zzylol and others added 2 commits March 19, 2026 20:13
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>
@zzylol zzylol changed the title docs: control plane design feat(controller): implement control plane in Rust (phases 1–4) Mar 20, 2026
zzylol and others added 2 commits March 19, 2026 20:47
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>
@zzylol zzylol changed the title feat(controller): implement control plane in Rust (phases 1–4) feat(controller): implement control plane in Rust (phases 1–4) + e2e test Mar 20, 2026
@zzylol
zzylol merged commit 7836abf into main Mar 20, 2026
@zzylol
zzylol deleted the 36-discussion-how-to-design-the-control-plane branch March 20, 2026 02:47
@zzylol

zzylol commented Mar 20, 2026

Copy link
Copy Markdown
Contributor Author

@milindsrivastava1997 FYI

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
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.

[Discussion] How to design the control plane?

1 participant