Skip to content

Commit c0ce934

Browse files
dougqhclaude
andcommitted
Collect bounded stats collapse counters before the unbounded span registry
The span-metric registry holds one entry per instrumentation name and can exceed the 1024-entry telemetry queue on its own. Draining it before the small, fixed set of client-side stats collapse counters could fill the queue and starve those counters indefinitely. Collect them first so they are always emitted; the pre-read capacity guard still protects their delta baseline. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 6431932 commit c0ce934

1 file changed

Lines changed: 26 additions & 22 deletions

File tree

internal-api/src/main/java/datadog/trace/api/telemetry/CoreMetricCollector.java

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,32 @@ private CoreMetricCollector() {
3333

3434
@Override
3535
public void prepareMetrics() {
36+
// Collect the bounded, high-value client-side trace-stats span-collapse counters first, tagged
37+
// by collapse reason. There is only a small, fixed set of these; the span-metric registry below
38+
// is unbounded (one entry per instrumentation name), so draining it first could fill the queue
39+
// and starve the collapse counters indefinitely under high instrumentation counts. Collecting
40+
// them up front guarantees they are emitted.
41+
for (StatsMetrics.TaggedCounter counter : this.statsMetrics.getTaggedCounters()) {
42+
if (this.metricsQueue.remainingCapacity() == 0) {
43+
// Queue full: stop before reading any more counters. getValueAndReset() below resets the
44+
// counter's delta baseline, so resetting one we then fail to enqueue would drop that delta
45+
// for good; the untouched counters are picked up on the next collection cycle.
46+
break;
47+
}
48+
long value = counter.getValueAndReset();
49+
if (value == 0) {
50+
// Skip not updated counters
51+
continue;
52+
}
53+
CoreMetric metric =
54+
new CoreMetric(
55+
METRIC_NAMESPACE, true, counter.getName(), "count", value, counter.getTag());
56+
if (!this.metricsQueue.offer(metric)) {
57+
// Stop adding metrics if the queue is full
58+
break;
59+
}
60+
}
61+
3662
// Collect span metrics
3763
for (SpanMetricsImpl spanMetrics : this.spanMetricRegistry.getSpanMetrics()) {
3864
String tag = INTEGRATION_NAME_TAG + spanMetrics.getInstrumentationName();
@@ -67,28 +93,6 @@ public void prepareMetrics() {
6793
break;
6894
}
6995
}
70-
71-
// Collect client-side trace-stats span-collapse metrics, tagged by collapse reason.
72-
for (StatsMetrics.TaggedCounter counter : this.statsMetrics.getTaggedCounters()) {
73-
if (this.metricsQueue.remainingCapacity() == 0) {
74-
// Queue full: stop before reading any more counters. getValueAndReset() below resets the
75-
// counter's delta baseline, so resetting one we then fail to enqueue would drop that
76-
// delta for good; the untouched counters are picked up on the next collection cycle.
77-
break;
78-
}
79-
long value = counter.getValueAndReset();
80-
if (value == 0) {
81-
// Skip not updated counters
82-
continue;
83-
}
84-
CoreMetric metric =
85-
new CoreMetric(
86-
METRIC_NAMESPACE, true, counter.getName(), "count", value, counter.getTag());
87-
if (!this.metricsQueue.offer(metric)) {
88-
// Stop adding metrics if the queue is full
89-
break;
90-
}
91-
}
9296
}
9397

9498
@Override

0 commit comments

Comments
 (0)