Add bounded cardinality limits for OTEL metrics collection#8846
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0d8158f5ac
ℹ️ 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".
| // rather than growing the dictionary without bound. A benign read-then-add race here may | ||
| // let _points exceed the limit by a small number of points, which is acceptable. | ||
| var existing = Volatile.Read(ref _overflowPoint); | ||
| return existing ?? CreateOverflowPoint(); |
There was a problem hiding this comment.
Sum overflowed observable measurements instead of overwriting
When max cardinality is exceeded for an ObservableCounter or ObservableUpDownCounter that reports multiple tagged measurements, this routes every excess tag set to the same MetricPoint. The observable update path (UpdateObservableCounter) stores the latest current value and computes deltas against that single last value, so overflow exports contain only the last excess series (or an incorrect delta) instead of the sum of all overflowed series; e.g. excess callbacks returning 10 and 20 export 20, not 30. The overflow point needs separate aggregation semantics for observable sums.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Ouch, this is a tricky one... It's valid, basically because we're now merging multiple series into a single point, so previous assumptions no apply.
Fixed by adding "special" behaviour for overflow points... it all feels a bit confusing/nasty... but I think it's "correct" per the expectations of the spec etc...
BenchmarksBenchmark execution time: 2026-07-02 14:46:12 Comparing candidate commit a08b505 in PR branch Found 0 performance improvements and 1 performance regressions! Performance is the same for 70 metrics, 1 unstable metrics, 60 known flaky benchmarks, 66 flaky benchmarks without significant changes.
|
Execution-Time Benchmarks Report ⏱️Execution-time results for samples comparing This PR (8846) and master. ✅ No regressions detected - check the details below Full Metrics ComparisonFakeDbCommand
HttpMessageHandler
Comparison explanationExecution-time benchmarks measure the whole time it takes to execute a program, and are intended to measure the one-off costs. Cases where the execution time results for the PR are worse than latest master results are highlighted in **red**. The following thresholds were used for comparing the execution times:
Note that these results are based on a single point-in-time result for each branch. For full results, see the dashboard. Graphs show the p99 interval based on the mean and StdDev of the test run, as well as the mean value of the run (shown as a diamond below the graph). Duration chartsFakeDbCommand (.NET Framework 4.8)gantt
title Execution time (ms) FakeDbCommand (.NET Framework 4.8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8846) - mean (69ms) : 67, 71
master - mean (72ms) : 67, 77
section Bailout
This PR (8846) - mean (73ms) : 71, 76
master - mean (77ms) : 73, 80
section CallTarget+Inlining+NGEN
This PR (8846) - mean (1,084ms) : 1034, 1133
master - mean (1,084ms) : 1040, 1128
FakeDbCommand (.NET Core 3.1)gantt
title Execution time (ms) FakeDbCommand (.NET Core 3.1)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8846) - mean (108ms) : 105, 111
master - mean (113ms) : 106, 120
section Bailout
This PR (8846) - mean (109ms) : 107, 111
master - mean (112ms) : 106, 117
section CallTarget+Inlining+NGEN
This PR (8846) - mean (780ms) : 751, 809
master - mean (780ms) : 751, 808
FakeDbCommand (.NET 6)gantt
title Execution time (ms) FakeDbCommand (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8846) - mean (100ms) : 95, 105
master - mean (101ms) : 95, 107
section Bailout
This PR (8846) - mean (98ms) : 94, 101
master - mean (99ms) : 94, 105
section CallTarget+Inlining+NGEN
This PR (8846) - mean (937ms) : 891, 983
master - mean (937ms) : 894, 980
FakeDbCommand (.NET 8)gantt
title Execution time (ms) FakeDbCommand (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8846) - mean (95ms) : 87, 104
master - mean (94ms) : 91, 97
section Bailout
This PR (8846) - mean (100ms) : 96, 105
master - mean (99ms) : 94, 103
section CallTarget+Inlining+NGEN
This PR (8846) - mean (817ms) : 773, 861
master - mean (814ms) : 764, 864
HttpMessageHandler (.NET Framework 4.8)gantt
title Execution time (ms) HttpMessageHandler (.NET Framework 4.8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8846) - mean (205ms) : 200, 209
master - mean (203ms) : 198, 207
section Bailout
This PR (8846) - mean (210ms) : 205, 215
master - mean (207ms) : 201, 212
section CallTarget+Inlining+NGEN
This PR (8846) - mean (1,225ms) : 1174, 1275
master - mean (1,213ms) : 1170, 1257
HttpMessageHandler (.NET Core 3.1)gantt
title Execution time (ms) HttpMessageHandler (.NET Core 3.1)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8846) - mean (293ms) : 286, 300
master - mean (291ms) : 282, 300
section Bailout
This PR (8846) - mean (294ms) : 288, 299
master - mean (290ms) : 285, 294
section CallTarget+Inlining+NGEN
This PR (8846) - mean (976ms) : 957, 996
master - mean (972ms) : 948, 996
HttpMessageHandler (.NET 6)gantt
title Execution time (ms) HttpMessageHandler (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8846) - mean (286ms) : 279, 292
master - mean (284ms) : 277, 290
section Bailout
This PR (8846) - mean (285ms) : 280, 290
master - mean (284ms) : 279, 288
section CallTarget+Inlining+NGEN
This PR (8846) - mean (1,177ms) : 1142, 1212
master - mean (1,174ms) : 1136, 1212
HttpMessageHandler (.NET 8)gantt
title Execution time (ms) HttpMessageHandler (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8846) - mean (285ms) : 277, 292
master - mean (283ms) : 277, 289
section Bailout
This PR (8846) - mean (283ms) : 275, 291
master - mean (285ms) : 278, 293
section CallTarget+Inlining+NGEN
This PR (8846) - mean (1,051ms) : 1005, 1096
master - mean (1,048ms) : 1002, 1093
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
64cf2c9 to
9386f37
Compare
9386f37 to
fcbada8
Compare
| new() | ||
| { | ||
| { ConfigurationKeys.FeatureFlags.OpenTelemetryMetricsCardinalityLimit, "1" }, | ||
| { ConfigurationKeys.OpenTelemetry.ExporterOtlpMetricsTemporalityPreference, "cumulative" }, |
There was a problem hiding this comment.
Thanks for including a cumulative aggregation temporality!
zacharycmontoya
left a comment
There was a problem hiding this comment.
Thanks for the improvement and the detailed test cases!
Adds an int config key (default 2000) controlling the maximum number of distinct attribute sets tracked per OTel metric stream. Parsed and exposed as TracerSettings.OpenTelemetryMetricsMaxCardinality; consumed in a later commit to bound metric-point cardinality. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
fcbada8 to
a08b505
Compare
Summary of changes
Add configuration for controlling cardinality of OTEL metrics
Reason for change
Currently we have unbounded cardinality of OTEL metrics collected using the
System.Diagnostics.Metricsnamespace. However, this can have implications for memory use, and in accordance to the spec, this PR adds a default 200 point limit, which can be overridden byDD_METRICS_OTEL_MAX_CARDINALITY.Implementation details
DD_METRICS_OTEL_MAX_CARDINALITYwith a default value of 2000Test coverage
Added a bunch of unit tests to demonstrate the behaviour
Other details
Designed to be used in conjunction with
Addresses