From 5e159d7e4bcad6932a49846fc5f603bb17c30f15 Mon Sep 17 00:00:00 2001 From: bm1549 Date: Mon, 23 Mar 2026 20:49:56 -0400 Subject: [PATCH 1/4] fix(tracing): format _dd.p.ksr with decimal notation instead of scientific notation Very small sampling rates (e.g. 0.000001) were formatted using Python's :.6g format which outputs scientific notation like "1e-06". This changes to explicit rounding at the integer level and :.6f formatting to always produce decimal notation with up to 6 decimal digits, trailing zeros stripped (e.g. "0.000001"). Fixes APMAPI-1869 Co-Authored-By: Claude Opus 4.6 (1M context) --- ddtrace/internal/sampling.py | 16 ++++++++++++++-- tests/tracer/test_sampler.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/ddtrace/internal/sampling.py b/ddtrace/internal/sampling.py index 53a63dc2770..8a2706c7e0b 100644 --- a/ddtrace/internal/sampling.py +++ b/ddtrace/internal/sampling.py @@ -1,4 +1,5 @@ import json +import math from typing import Any from typing import Optional from typing import TypedDict @@ -46,6 +47,17 @@ class PriorityCategory(object): KNUTH_SAMPLE_RATE_KEY = "_dd.p.ksr" + +def _format_ksr(rate: float) -> str: + """Format a sampling rate for _dd.p.ksr: up to 6 decimal digits, trailing zeros stripped. + + Uses explicit rounding via math.floor(x + 0.5) to avoid Python's banker's rounding + which would round 0.0000005 down to 0 instead of up to 0.000001. + """ + rounded = math.floor(rate * 1e6 + 0.5) / 1e6 + return f"{rounded:.6f}".rstrip("0").rstrip(".") + + SpanSamplingRules = TypedDict( "SpanSamplingRules", { @@ -243,10 +255,10 @@ def _set_sampling_tags(span: Span, sampled: bool, sample_rate: float, mechanism: SamplingMechanism.REMOTE_DYNAMIC_TRACE_SAMPLING_RULE, ): span._set_attribute(_SAMPLING_RULE_DECISION, sample_rate) - span._set_attribute(KNUTH_SAMPLE_RATE_KEY, f"{sample_rate:.6g}") + span._set_attribute(KNUTH_SAMPLE_RATE_KEY, _format_ksr(sample_rate)) elif mechanism == SamplingMechanism.AGENT_RATE_BY_SERVICE: span._set_attribute(_SAMPLING_AGENT_DECISION, sample_rate) - span._set_attribute(KNUTH_SAMPLE_RATE_KEY, f"{sample_rate:.6g}") + span._set_attribute(KNUTH_SAMPLE_RATE_KEY, _format_ksr(sample_rate)) # Set the sampling priority priorities = SAMPLING_MECHANISM_TO_PRIORITIES[mechanism] priority_index = _KEEP_PRIORITY_INDEX if sampled else _REJECT_PRIORITY_INDEX diff --git a/tests/tracer/test_sampler.py b/tests/tracer/test_sampler.py index 9c43c5d60d7..2d3ccb1444f 100644 --- a/tests/tracer/test_sampler.py +++ b/tests/tracer/test_sampler.py @@ -889,3 +889,32 @@ def span(): def test_trace_tag(span, sampling_mechanism, expected): span._set_sampling_decision_maker(sampling_mechanism) assert span.context._meta["_dd.p.dm"] == expected + + +@pytest.mark.parametrize( + "sample_rate,expected_ksr", + [ + (1.0, "1"), + (0.5, "0.5"), + (0.000001, "0.000001"), + (0.0000001, "0"), + (0.0000005, "0.000001"), + (0.7654321, "0.765432"), + ], + ids=[ + "rate_1_strips_trailing_zeros", + "simple_rate", + "six_decimal_precision_boundary", + "below_precision_rounds_to_zero", + "rounds_up_to_one_millionth", + "truncation_at_six_decimals", + ], +) +def test_ksr_formatting(span, sample_rate, expected_ksr): + """_dd.p.ksr is formatted with up to 6 decimal digits, trailing zeros stripped, no scientific notation.""" + from ddtrace.internal.sampling import KNUTH_SAMPLE_RATE_KEY + from ddtrace.internal.sampling import SamplingMechanism + from ddtrace.internal.sampling import _set_sampling_tags + + _set_sampling_tags(span, True, sample_rate, SamplingMechanism.LOCAL_USER_TRACE_SAMPLING_RULE) + assert span.context._meta.get(KNUTH_SAMPLE_RATE_KEY) == expected_ksr From a2534e625f6251f3283c0fee1e110ec472737ddd Mon Sep 17 00:00:00 2001 From: bm1549 Date: Mon, 23 Mar 2026 20:50:27 -0400 Subject: [PATCH 2/4] chore: add release note for KSR formatting fix Co-Authored-By: Claude Opus 4.6 (1M context) --- .../notes/fix-ksr-scientific-notation-e7d1adabe548eb40.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 releasenotes/notes/fix-ksr-scientific-notation-e7d1adabe548eb40.yaml diff --git a/releasenotes/notes/fix-ksr-scientific-notation-e7d1adabe548eb40.yaml b/releasenotes/notes/fix-ksr-scientific-notation-e7d1adabe548eb40.yaml new file mode 100644 index 00000000000..42c78b185c6 --- /dev/null +++ b/releasenotes/notes/fix-ksr-scientific-notation-e7d1adabe548eb40.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + tracing: Fix ``_dd.p.ksr`` span tag formatting for very small sampling rates. + Previously, rates below 0.001 could be output in scientific notation (e.g. + ``1e-06``). Now always uses decimal notation with up to 6 decimal digits. From 4505c373287614e629775e9430a59f4b10bacf4c Mon Sep 17 00:00:00 2001 From: bm1549 Date: Mon, 23 Mar 2026 21:25:09 -0400 Subject: [PATCH 3/4] fix(test): check span._meta instead of span.context._meta for KSR tag The _set_attribute method stores string tags in span._meta, not span.context._meta. The CI failure showed the KSR values were correct but the assertion was reading from the wrong location. Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/tracer/test_sampler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tracer/test_sampler.py b/tests/tracer/test_sampler.py index 2d3ccb1444f..a4c7d59273f 100644 --- a/tests/tracer/test_sampler.py +++ b/tests/tracer/test_sampler.py @@ -917,4 +917,4 @@ def test_ksr_formatting(span, sample_rate, expected_ksr): from ddtrace.internal.sampling import _set_sampling_tags _set_sampling_tags(span, True, sample_rate, SamplingMechanism.LOCAL_USER_TRACE_SAMPLING_RULE) - assert span.context._meta.get(KNUTH_SAMPLE_RATE_KEY) == expected_ksr + assert span._meta.get(KNUTH_SAMPLE_RATE_KEY) == expected_ksr From fbfb615c3ca2eec1fc9543906b97d9c9a82485d2 Mon Sep 17 00:00:00 2001 From: bm1549 Date: Tue, 24 Mar 2026 07:01:41 -0400 Subject: [PATCH 4/4] ci: retry CI Co-Authored-By: Claude Opus 4.6 (1M context)