fix: archive tier covers all 5 sketched metrics (HLL/CS/CMS/KLL/DDSketch all in Thanos) - #350
Merged
Merged
Conversation
…plans (#46) The backend's POST /api/v1/storage_routing handler is an atomic per-tenant SWAP — every push replaces the whole tenant's routing table. The pre-existing handle_plan path emitted a single-element metrics:[…] document per call, so when the demo POSTed /api/v1/plan for each of the 5 sketched contract metrics in sequence only the LAST metric's entry survived in the backend. The other 4 defaulted to sketch_warm_tier (no warm-tier sketch state for archive-shape queries) → accuracy_reduce.py logged archive_miss for those metrics even though gorillas3 wrote their TSDB blocks to MinIO and Thanos had them indexed. Wire AppState.backend_routing_cache so every plan-emit cycle posts the union of all metrics planned to date. Verified end-to-end against a live stack: storage_routing entries=5 after the per-metric POSTs, Thanos /api/v1/label/__name__/values lists all 5 sketched metrics plus the freshness probes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
zzylol
added a commit
that referenced
this pull request
May 8, 2026
…#351) PR #112 (freshness probe cache) and PR #350 (storage routing cumulative) both flagged producer→agent gRPC failures with high-cardinality batches: rpc error: code = ResourceExhausted desc = grpc: received message after decompression larger than max (X vs. 4194304) The gateway already has `max_recv_msg_size_mib: 64` (controller emit at stage_config.rs:428 + the corresponding gateway placeholder), but the AGENT receiver inherits the 4 MiB grpc default. With 5K series × 500 user_ids on the new HLL workload, batches exceed 4 MiB and the agent silently drops them — no metric flow → no sketch state → no probe data. Three sites: - `deploy/configs/asap-otel-agent-b6-asap-single-sketch.yaml` (static placeholder loaded via volume mount) - `controller/src/config/stage_config.rs:128` (typed Edge emit, the legacy single-pipeline arm) - `controller/src/config/stage_config.rs:852` (typed Edge emit, the 5-sketch routing-connector arm) OTAP variant (`stage_config_otap.rs`) uses a different listener_addr shape; left untouched — needs a separate look. Refs #46. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merged
3 tasks
zzylol
added a commit
that referenced
this pull request
May 9, 2026
…e (was single ddsketch) (#353) PR #350's report flagged that OpAMP RemoteConfig push isn't applied by the agent at runtime — the effective-config preview keeps showing the static bootstrap YAML loaded from the volume mount, so the running pipeline is whatever this static file declares. Previously the placeholder declared a single [gorillas3, ddsketch, batch] pipeline, so agents only ran ONE sketch (DDSketch) regardless of the controller plan. KLL / HLL / CountSketch / CountMinSketch processors never ran. This PR rewrites the static placeholder to mirror the typed-emit shape from controller/src/config/stage_config.rs::emit_edge_yaml_5sketch_routing: all 5 sketch processors loaded at top level, the OTel v0.106 routing connector under connectors: (NOT processors:), and 7 named pipelines (entry + raw_passthrough + 5 per-family paths). Each per-sketch pipeline runs gorillas3 first so the cold-tier write happens on raw samples before the sketch processor mutates the stream. The agent boots cleanly with no config-load errors — verified with `asap-otel validate` (exit 0) and a minimal compose stack run that reports "Everything is ready. Begin running and processing data." with all 5 sketch processors listed under their respective pipelines. Smoke test in deploy/configs/tests/ parses the YAML and pins: - All 5 sketch processors present at top level - routing in connectors: (not processors:) - All named pipelines (entry + raw_passthrough + 5 per-family) - Each per-sketch pipeline has gorillas3 first - routing.default_pipelines == [metrics/raw_passthrough] - context: metric on every routing table entry Refs #46. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
zzylol
added a commit
that referenced
this pull request
May 9, 2026
#356) PR #355 lowered gorillas3 `window_interval` to 5 s to keep the in-memory windowState within the agent's 1.5 GiB cgroup, but under sustained load six per-family pipelines still overshoot the ceiling and the agent gets OOM-killed (exit 137) after roughly three minutes — the demo confirmed both `agent-a-1` and `agent-b-1` Exited (137) two minutes into Phase 2, and the backend's `MEMORY_DIAG` reported `group_states_len=0` for every worker because nothing ever ingested. The OpenTelemetry Collector ships a `memory_limiter` processor that refuses incoming data when RSS crosses a soft threshold, applying backpressure to upstream senders rather than crashing. The gateway already uses it (see compose log: `Memory limiter configured limit_mib=1536 spike_limit_mib=256 check_interval=1`); the agent didn't. This change inserts `memory_limiter` AS THE FIRST processor in every per-sketch pipeline (and the default `metrics/raw_passthrough`): metrics/ddsketch_path: processors: [memory_limiter, gorillas3, ddsketch, batch] Same for `kll_path`, `hll_path`, `countsketch_path`, `countminsketch_path`, and `raw_passthrough`. The entry pipeline (which has `processors: []` because routing fan-out is the connector's job) is unchanged. Threshold rationale: agent cgroup is 1536 MiB, so we pin `limit_mib: 1280` (≈ 80 % of cgroup) and `spike_limit_mib: 256` — leaving 256 MiB headroom under the cgroup ceiling for short bursts. Mirrors the gateway shape but scaled to the agent's smaller cgroup. Updates both the static placeholder (`deploy/configs/asap-otel-agent-b6-asap-single-sketch.yaml`) and the controller's typed emit (`emit_edge_yaml_5sketch_routing` in `controller/src/config/stage_config.rs`) so once the OpAMP-push gap flagged by PR #350 lands, the runtime swap stays in shape. Tests: - New `mvp46_per_sketch_pipelines_have_memory_limiter_first` Rust test asserts every per-family pipeline + raw_passthrough lists `memory_limiter` first. - Python smoke `test_static_placeholder_5sketch_routing.py` gains `test_memory_limiter_processor_block_present` and updated `test_each_per_sketch_pipeline_has_memory_limiter_first` / `test_raw_passthrough_pipeline_shape` covering the new shape. Refs #46, follow-up to #355. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 17, 2026
zzylol
added a commit
that referenced
this pull request
May 17, 2026
…art (ASAPCollector#381) (#383) Patches the upstream opampextension's `onMessage` callback to handle `msg.RemoteConfig` — closes the residual half of PR #350. Background. The agent connects to the controller's OpAMP server, receives `AgentRemoteConfig` messages, but the upstream extension's onMessage handler only processes `AgentIdentification` and `CustomMessage`. RemoteConfig is silently dropped. Result: the controller's elaborate typed-stage-split runtime YAML (routing connector + 5 sketch processors + gorillas3) is generated, pushed, and ignored — every MVP demo since runs the agent's static bootstrap YAML, the central blocker tracked in this repo's #381. Patch (overlay at `opentelemetry-collector-contrib-patch/extension/opampextension/`): * `config.go` — new `RemoteConfigPath string` field (mapstructure key `remote_config_path`). When empty, the extension stays "report-only" (upstream behavior preserved). When set, the extension applies pushed configs by writing to that path. * `opamp_agent.go`: - `onMessage` now dispatches `msg.RemoteConfig` to a new `processRemoteConfig` method when both `msg.RemoteConfig` and `cfg.RemoteConfigPath` are non-nil/non-empty. - `Start` advertises `AcceptsRemoteConfig` capability when `RemoteConfigPath` is set. The OpAMP client library at `client/internal/receivedprocessor.go:83` silently drops `RemoteConfig` without this capability — that was the first dead-end on debug. - `processRemoteConfig` picks the first non-empty ConfigFile body (preferring `""` / `"asap-otel"` keys), YAML-validates it, writes directly to disk (NOT write-tmp+rename — Linux returns EBUSY on `renameat2` for Docker bind-mounted files), reports `RemoteConfigStatuses_APPLIED`, sleeps 200ms to flush the status, and `os.Exit(0)`. Docker `restart=unless-stopped` / systemd `Restart=always` is expected to bring the collector back up loading the freshly-written YAML. Non-code files (`auth.go`, `factory.go`, `logger.go`, etc.) are verbatim copies of the upstream extension. OCB's per-extension module replace requires the WHOLE module directory under the replace path; we can't just override two files. builder-config.yaml: adds `path: ./extension/opampextension` beneath the existing `gomod:` line so OCB redirects to the patch overlay. Mirrors the pattern every other patched processor uses (ddsketchprocessor, kllprocessor, …). End-to-end verification (single-node MVP smoke test under /mydata/mvp-smoke-test/): * Before patch: agent stays on bootstrap YAML; `restartCount=0`; backend sees sketches keyed by full wire-attr fingerprint. * After patch: agent applies pushed config and exits; `restartCount=1+`; host-side mounted `agent.yaml` is overwritten with the controller-emitted typed-stage-split runtime YAML. The agent's post-restart behavior surfaces downstream issues (gorillas3 S3 bucket not provisioned in smoke setup, memory limiter trips on the 5-sketch-pipeline workload) — those are separate blockers, not in scope for this PR. The OpAMP-apply half of #381 is what this PR closes. Companion: ASAPQuery-backend `b1-5-controller-emit-literal-env-vars` branch — switches the controller's gorillas3 YAML emit from bash-style `${VAR:-default}` (rejected by OTel's confmap URI parser) to literal values resolved from the controller's env at emit time, and threads `remote_config_path: /etc/otel/config.yaml` into every emitted opamp extension block so OpAMP-apply persists across the restart loop. 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
Fixes the post-#345 demo's
archive_miss=1370failure mode for the four counter-sketch metrics (unique_users_per_min,top_endpoint_qps,endpoint_request_freq,request_size_bytes).Root cause (a): the backend's
POST /api/v1/storage_routinghandler is an atomic per-tenant SWAP — every push replaces the whole tenant's routing table.handle_planemitted a single-elementmetrics:[…]document per call, so when the demo POSTed/api/v1/planfor each of the 5 sketched contract metrics in sequence, only the last metric's entry survived in the backend. The other 4 metrics defaulted tosketch_warm_tier(which has no warm-tier sketch state for archive-shape queries:count,topk,rate_post_hoc,histogram_quantile,delta,deriv,absent) → backend returned empty / 404 →accuracy_reduce.pyloggedarchive_misseven though gorillas3 did write their TSDB blocks to MinIO and Thanos had them indexed.Fix: thread per-metric
BackendStageConfigthrough a newAppState.backend_routing_cache: Arc<Mutex<HashMap<String, BackendStageConfig>>>. Each plan-emit cycle updates the cache for the metric being planned and re-emits the storage-routing JSON from the union of all known plans. The next swap then preserves every previously seen metric's routing entry.Scope: controller-only (
controller/src/main.rs). Doesn't touch gorillas3, deploy/configs, or backend.Evidence
Before (live demo stack with all 5 producers emitting):
After (same stack, fix applied, controller rebuilt):
Regression test
api_tests::storage_routing_cumulative_push_covers_all_5_sketched_metrics(incontroller/src/main.rs):POST /api/v1/storage_routingbody./api/v1/plan5 times for the 5 sketched contract metrics.metrics:[…]array contains all 5 metrics, each carrying athanos_archivetarget.What this PR does NOT fix
Out-of-scope failures observed during diagnosis but not within this PR's blast radius:
[gorillas3, ddsketch, batch]single-pipeline. This is upstream of the routing fix and orthogonal — the bootstrap config has gorillas3 first so all metrics still flow through it.grpc: received message after decompression larger than max 4194304): withEXPORTER_FIVE_SKETCH_USER_POOL=1000×EXPORTER_CARDINALITY=500the OTLP delta export overflows the agent's 4 MiBmax_recv_msg_size. Tuning belongs indeploy/configs/asap-otel-agent-*.yaml(parallel agent's scope).Test plan
cargo test --bin controller storage_routing(all 16 pass, including new regression)POST /api/v1/plan→ backendstorage_routingshowsmetrics_count=5(was 1 pre-fix)docker exec ... mc ls -r local/asap-gorilla-tsdb/shows TSDB blocks (gorillas3 unchanged, still writes for all metrics)curl thanos /api/v1/label/__name__/valuesshows all 5 sketched metrics plus freshness probesdocker logs backend | grep "swap completed"shows entries grow monotonically across cumulative postsRefs #46.
🤖 Generated with Claude Code