fix(otlp): preserve empty metric descriptors under non-matching policies - #303
Open
detail-app[bot] wants to merge 1 commit into
Open
detail-app[bot] wants to merge 1 commit into
detail-app[bot] wants to merge 1 commit into
Conversation
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a narrowly scoped OTLP metrics bug fix with regression coverage for both protobuf and JSON paths. It preserves valid empty descriptors while retaining existing policy-driven pruning, without introducing schema, infrastructure, or sensitive-data changes. You can add or adjust custom eligibility rules. Learn more. |
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.
Detail bug report: View on Detail
Fixes ENG-704
Bug
When any metric policy was loaded (even one matching nothing), the OTLP proxy silently dropped metric descriptors whose
dataoneof was set but whosedata_pointslist was empty — a valid OTLP wire form (a named metric with type/unit/description but no measurements this batch).dropped_countstayed0, so the loss was invisible to monitoring.Two compounding defects in
src/signals/otlp/metrics.zig:filterMetricsInPlacepruned any metric withdata != null and dataPointCount == 0, but checked only the post-filter count — it could not distinguish a metric that arrived empty (a valid descriptor) from one emptied by policy. This contradicted the code's own comment ("A metric with no data at all is left alone").processProtobufMetricsalways re-encoded (no "nothing-modified" fast path), so the prune ran even when no policy matched — unlikeprocessProtobufLogs, which returns the original bytes verbatim when nothing was modified.Fix
filterMetricsInPlace: captureoriginal_dp = dataPointCount(metric)before per-data-point filtering, and prune only whenoriginal_dp > 0 and dataPointCount == 0— i.e. only metrics emptied by policy are pruned; originally-empty descriptors survive. This fix is shared by the protobuf and JSON paths.processProtobufMetrics, mirroringprocessProtobufLogs: whencounts.dropped_count == 0, return the original bytes without re-encoding. This restores logs/metrics parity and prevents the prune from firing for non-matching policies.Testing
zig build test --summary all, Debug and ReleaseSafe): 522/523 pass, 1 skip (the pre-existing S3/MinIO e2e test that self-skips viareturn error.SkipZigTest; unrelated to this change). Added 3 regression tests:zig build,zig build -Doptimize=ReleaseSafe,zig fmt --check src/, andziglintall pass clean.edge-otlpand the repo'secho-server(as a live upstream), loaded a non-matching metric policy, and generated valid OTLP payloads withprotobuf+opentelemetry-proto. The upstream captured both the populated gauge and the empty-gauge descriptor (53 bytes, byte-identical to input — confirming the streaming path returns.keep, not.replace). Repeating with a policy that drops one metric by name confirmed the emptied metric was pruned while the originally-empty descriptor survived. As a final check, rebuildingedge-otlpwith the fix reverted reproduced the bug end-to-end (upstream received only the populated gauge, 36 bytes), then the restored fix forwarded both metrics again.Automatic Fixes PRs can be configured here.