feat(sync-service): tail-drop empty shape-GET response spans#4682
feat(sync-service): tail-drop empty shape-GET response spans#4682alco wants to merge 10 commits into
Conversation
Pure function deciding the SampleRate weight for a shape-GET root span so the spans of empty/up-to-date responses can be tail-dropped. Keep-on-error wins first (5xx -> SampleRate=1), empty non-SSE 2xx responses get the SampleRate=0 drop sentinel when enabled, everything else is left unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an OTel span processor whose on_end/2 returns :dropped for spans carrying SampleRate=0, and register it ahead of otel_batch_processor. The SDK folds on_end with a short-circuiting andalso, so a non-true return before the batch processor prevents the span from ever being queued for export. Only empty-response spans carry the sentinel; all others pass through unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire the empty-response drop into the request-end SampleRate stamp: empty, non-SSE 2xx shape-GET responses are stamped with the SampleRate=0 sentinel so the drop processor skips them at export time. Gated by the new ELECTRIC_DROP_EMPTY_RESPONSE_SPANS env var (default true); set it to false to export those spans normally. Empty/up-to-date responses are ~95% of shape-GET root spans in production, so dropping them substantially cuts exported trace volume. Error (5xx) and SSE responses are never dropped. Ratio-based keep-sampling is deferred to the cloud worker, which will propagate its rate to the origin via tracestate. Fixes #4664 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4682 +/- ##
==========================================
- Coverage 60.20% 59.41% -0.79%
==========================================
Files 410 394 -16
Lines 44378 43283 -1095
Branches 12581 12331 -250
==========================================
- Hits 26716 25718 -998
+ Misses 17584 17523 -61
+ Partials 78 42 -36
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Claude Code ReviewSummaryTail-drops the OTel spans of empty/up-to-date shape-GET responses via a What's Working Well
Issues FoundCritical (Must Fix)None identified. Important (Should Fix)None identified. Suggestions (Nice to Have)1. Minor: 2. Orphaned-child robustness comment (carried over, still optional). The processor drops only the single span carrying 3. Positional booleans in Issue ConformanceFixes #4664. Ships the all-or-nothing boolean ( Previous Review Status
Review iteration: 5 | 2026-07-03 |
Extend the otel-export lux suite: an already-up-to-date request returns an empty no-change response, and the suite asserts a data Plug_shape_get root span is exported (shape_req.is_empty_response=false) while the empty one is never exported (no Plug_shape_get span with is_empty_response=true reaches the collector). Exercises the drop end-to-end through the real OTel export pipeline. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extract request_and_response/1 so open_telemetry_attrs/1 and response_trace_attrs/1 share a single resolver for the request/response structs instead of each re-deriving them from the conn assigns, keeping the two in sync. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The empty-response assertion was exercising the wrong path: ot_is_empty_response is set only by no_change_response, which is reached only when a live long-poll times out with no new data. A plain non-live request at the current offset returns is_empty_response=false, so the drop was never triggered and the assertion passed vacuously. Use a live request that long-polls until timeout, and assert its body is the up-to-date control only (no data element) so the drop check is meaningful. Also replace the bare "0" count match, which flaked on lux prompt-gluing (SH-PROMPT:0), with the suite's `test ... && echo TOKEN` / `??TOKEN` idiom. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Flip ELECTRIC_DROP_EMPTY_RESPONSE_SPANS to default false so the drop ships dormant and is opt-in per environment: merging the code changes nothing until an operator sets it true. The otel-export integration test now enables it explicitly to exercise the drop. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Expose the existing long_poll_timeout config value via ELECTRIC_LONG_POLL_TIMEOUT so it can be tuned per environment, e.g. shortened in integration tests to trigger an empty long-poll response quickly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address review feedback: remove over-explanatory module/inline comments, drop references to the private worker repo and to SDK-internal fold mechanics, and stop restating default values in comments. Simplify the otel-export lux test to trigger the empty long-poll response via a short ELECTRIC_LONG_POLL_TIMEOUT instead of waiting out the default, and tighten its comments. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add `empty` to the [:electric, :plug, :serve_shape] event metadata and to the electric.plug.serve_shape.requests.count metric's tags. This keeps a precise count of empty vs non-empty responses via metrics even when their spans are dropped (ELECTRIC_DROP_EMPTY_RESPONSE_SPANS=true), since the metric path is independent of span sampling. The otel-export test asserts the dropped empty response is still counted with empty=true in the exported metric. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
What
Tail-drop the OpenTelemetry spans of empty / up-to-date shape-GET responses at the origin, to cut exported trace volume. Empty responses are ~95% of
Plug_shape_getroot spans in production, yet carry no useful trace detail. Part of the ongoing effort to reduce Honeycomb trace volume.How
Electric.Telemetry.EmptyResponseSampler— a pure, unit-tested decision function for the root span'sSampleRateweight. In order:status >= 500→SampleRate = 1, never dropped (keep-on-error wins).SampleRate = 0(the drop sentinel).Electric.Telemetry.OpenTelemetry.EmptyResponseDropProcessor— an:otel_span_processorwhoseon_end/2returns:droppedfor spans stampedSampleRate = 0. Registered ahead of the exporting processors so that a dropped span is never queued for export. Only empty-response spans carry the sentinel; everything else passes through.Config
ELECTRIC_DROP_EMPTY_RESPONSE_SPANS(boolean, defaultfalse) — opt-in; set it totrueto enable the drop. Error (5xx) and SSE responses are never dropped.ELECTRIC_LONG_POLL_TIMEOUT(integer, ms) — exposes the existing long-poll timeout as a config knob (used by the integration test to trigger an empty long-poll response quickly).Fixes #4664
🤖 Generated with Claude Code