fix(metrics): preserve exact sum/avg when converting histogram buckets to DDSketch#25752
fix(metrics): preserve exact sum/avg when converting histogram buckets to DDSketch#25752vladimir-dd wants to merge 8 commits into
Conversation
|
All contributors have signed the CLA ✍️ ✅ |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7e06e7d26b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
7e06e7d to
82f2774
Compare
|
recheck |
|
I have read the CLA Document and I hereby sign the CLA |
…s to DDSketch AggregatedHistogram already tracks an exact running sum/count as each observation is recorded (see Histogram::record). transform_to_sketch discarded both and rebuilt approximate versions purely from bucket-boundary interpolation, which is a poor approximation when a bucket's true values sit far from its edges (e.g. nearly all mass in an unbounded first/last bucket, where interpolation collapses the bucket to a point mass at its single finite edge). count already survives interpolation exactly (interpolation always redistributes the exact input count, never losing or gaining any), so only override sum/avg with the values the source histogram already had. quantile() is unaffected since it only reads the sketch bins, not sum/count. Signed-off-by: Vladimir Zhuk <vladimir.zhuk@datadoghq.com>
82f2774 to
012d914
Compare
- Use the histogram's own exact count (not the sketch's bucket-derived count) as the divisor for avg, since sources like Prometheus drop their cumulative +Inf bucket when converting to deltas, which can make the bucket-derived count undercount relative to the true total. - Represent OTLP's optional histogram sum as NaN instead of defaulting to an exact 0.0, so the sum/avg override is skipped (falling back to the bucket-derived estimate) rather than corrupting non-empty histograms that legitimately omit sum. - Extend min/max to keep them consistent with the now-exact avg, since interpolation can otherwise report an impossible avg outside [min, max] (most notably for the unbounded first/last bucket).
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f8aea1e2ae
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
f8aea1e to
5ab4c00
Compare
Co-authored-by: Bruce Guenter <bruce.guenter@datadoghq.com>
Summary
AgentDDSketch::transform_to_sketchconverts anAggregatedHistograminto aDDSketchfor sinks likedatadog_metrics. It rebuildssum/avgpurely by interpolating within each bucket's boundaries, discarding the exactsum/countthatHistogram::recordalready tracked as each observation came in.This breaks down badly when a bucket's true values sit far from its edges — most notably the unbounded first/last bucket, which
insert_interpolate_bucketscollapses to a point mass at its single finite edge. If the real observations are much smaller (first bucket) or larger (last bucket) than that edge, the reconstructedsum/avgcan be off by multiple orders of magnitude, even thoughcountsurvives interpolation exactly (interpolation always redistributes the exact input count, never losing or gaining any).Example
Two real observations of
source_send_latency_seconds, each ~18µs:sum=0.000036183s (36.183µs total)count=2avg=0.0000180915s (18.09µs)The histogram's buckets for this window:
upper_limit = 2⁻¹²(244.14µs),count = 2— both observations land here, since it's the smallest bucketupper_limit = +Inf,count = 0source_send_latency_secondsand similar sub-millisecond timers routinely record values well under 244µs, so in practice their entire mass — count and all — piles into this one unbounded-below bucket, which is exactly the case this bug hits hardest.So
avgwas reported as ~244µs when the true average was ~18µs — off by >13x.This exact scenario (two observations, both far below the first bucket's edge) is what
test_transform_to_sketch_preserves_exact_sum_for_unbounded_first_bucketreproduces.Fix
Override sum/avg with the source histogram's own exact sum/count. quantile() only reads the sketch's bins, not sum/count, so this doesn't affect percentile correctness — it only fixes sum/avg.
Additionally, based on review feedback:
avg=sum/sketch.count()would inflate avg, and takingavg=sum/true_countwould break the invariantavg=sum/countin the DD sketch.Side effect
Test plan
test_transform_to_sketch_preserves_exact_sum_for_unbounded_first_bucket, reproducing the worst case (two observations, both far below the first bucket's finite edge) and asserting the sketch'ssum/avgmatch the source histogram exactly instead of the ~1260x-inflated bucket-derived value.ddsketchtest suite passes unchanged.Change Type
Is this a breaking change?
Does this PR include user facing changes?
no-changeloglabel to this PR.References
Notes
@vectordotdev/vectorto reach out to us regarding this PR.pre-pushhook, please see this template.make fmtmake check-clippy(if there are failures it's possible some of them can be fixed withmake clippy-fix)make testgit merge origin masterandgit push.Cargo.lock), pleaserun
make build-licensesto regenerate the license inventory and commit the changes (if any). More details on the dd-rust-license-tool.