From 8637533b69f23e138ba421cf2e2c94d3c5bcb0bd Mon Sep 17 00:00:00 2001 From: bm1549 Date: Tue, 10 Mar 2026 21:39:32 -0400 Subject: [PATCH 1/5] Add _dd.p.ksr propagated tag for Knuth sampling rate Co-Authored-By: Claude Opus 4.6 --- ext/priority_sampling/priority_sampling.c | 15 +++++++++++ .../025-ksr-tag-rule-sampling.phpt | 24 ++++++++++++++++++ .../026-ksr-tag-default-sampling.phpt | 23 +++++++++++++++++ .../027-ksr-tag-not-set-manual.phpt | 25 +++++++++++++++++++ .../028-ksr-tag-formatting.phpt | 18 +++++++++++++ 5 files changed, 105 insertions(+) create mode 100644 tests/ext/priority_sampling/025-ksr-tag-rule-sampling.phpt create mode 100644 tests/ext/priority_sampling/026-ksr-tag-default-sampling.phpt create mode 100644 tests/ext/priority_sampling/027-ksr-tag-not-set-manual.phpt create mode 100644 tests/ext/priority_sampling/028-ksr-tag-formatting.phpt diff --git a/ext/priority_sampling/priority_sampling.c b/ext/priority_sampling/priority_sampling.c index 57f845fe27f..a12953f6416 100644 --- a/ext/priority_sampling/priority_sampling.c +++ b/ext/priority_sampling/priority_sampling.c @@ -7,6 +7,7 @@ #include #include "../configuration.h" +#include "../tracer_tag_propagation/tracer_tag_propagation.h" #include "../limiter/limiter.h" #include "ddshared.h" @@ -62,6 +63,18 @@ static void dd_update_decision_maker_tag(ddtrace_root_span_data *root_span, } } +static void dd_update_knuth_sampling_rate_tag(ddtrace_root_span_data *root_span, double sample_rate) { + zend_array *meta = ddtrace_property_array(&root_span->property_meta); + + char buf[32]; + snprintf(buf, sizeof(buf), "%.6g", sample_rate); + + zval ksr; + ZVAL_STRING(&ksr, buf); + zend_hash_str_update(meta, ZEND_STRL("_dd.p.ksr"), &ksr); + zend_hash_str_add_empty_element(ddtrace_property_array(&root_span->property_propagated_tags), ZEND_STRL("_dd.p.ksr")); +} + static bool dd_check_sampling_rule(zend_array *rule, ddtrace_span_data *span) { zval *service = &span->property_service; zval *resource = &span->property_resource; @@ -320,6 +333,7 @@ static void dd_decide_on_sampling(ddtrace_root_span_data *span) { zend_hash_str_del(metrics, ZEND_STRL("_dd.rule_psr")); } else { zend_hash_str_update(metrics, ZEND_STRL("_dd.rule_psr"), &sample_rate_zv); + dd_update_knuth_sampling_rate_tag(span, sample_rate); } zend_hash_str_del(metrics, ZEND_STRL("_dd.agent_psr")); @@ -329,6 +343,7 @@ static void dd_decide_on_sampling(ddtrace_root_span_data *span) { priority = sampling && !limited ? PRIORITY_SAMPLING_AUTO_KEEP : PRIORITY_SAMPLING_AUTO_REJECT; zend_hash_str_update(metrics, ZEND_STRL("_dd.agent_psr"), &sample_rate_zv); + dd_update_knuth_sampling_rate_tag(span, sample_rate); } if (limited) { diff --git a/tests/ext/priority_sampling/025-ksr-tag-rule-sampling.phpt b/tests/ext/priority_sampling/025-ksr-tag-rule-sampling.phpt new file mode 100644 index 00000000000..eab7800ff73 --- /dev/null +++ b/tests/ext/priority_sampling/025-ksr-tag-rule-sampling.phpt @@ -0,0 +1,24 @@ +--TEST-- +_dd.p.ksr propagated tag is set for rule-based sampling +--ENV-- +DD_TRACE_SAMPLING_RULES=[{"sample_rate": 0.3}] +DD_TRACE_GENERATE_ROOT_SPAN=1 +--FILE-- +metrics["_dd.rule_psr"] == 0.3) { + echo "Rule OK\n"; +} else { + var_dump($root->metrics); +} + +echo "_dd.p.ksr = ", isset($root->meta["_dd.p.ksr"]) ? $root->meta["_dd.p.ksr"] : "-", "\n"; +echo "_dd.p.dm = ", isset($root->meta["_dd.p.dm"]) ? $root->meta["_dd.p.dm"] : "-", "\n"; +?> +--EXPECTREGEX-- +Rule OK +_dd.p.ksr = 0.3 +_dd.p.dm = (-3|-) diff --git a/tests/ext/priority_sampling/026-ksr-tag-default-sampling.phpt b/tests/ext/priority_sampling/026-ksr-tag-default-sampling.phpt new file mode 100644 index 00000000000..4303e2546f0 --- /dev/null +++ b/tests/ext/priority_sampling/026-ksr-tag-default-sampling.phpt @@ -0,0 +1,23 @@ +--TEST-- +_dd.p.ksr propagated tag is set for default/agent-based sampling +--ENV-- +DD_TRACE_GENERATE_ROOT_SPAN=1 +--FILE-- +metrics["_dd.agent_psr"] === 1.0) { + echo "Agent PSR OK\n"; +} else { + echo "Agent PSR missing\n"; +} + +echo "_dd.p.ksr = ", isset($root->meta["_dd.p.ksr"]) ? $root->meta["_dd.p.ksr"] : "-", "\n"; +echo "_dd.p.dm = {$root->meta["_dd.p.dm"]}\n"; +?> +--EXPECT-- +Agent PSR OK +_dd.p.ksr = 1 +_dd.p.dm = -0 diff --git a/tests/ext/priority_sampling/027-ksr-tag-not-set-manual.phpt b/tests/ext/priority_sampling/027-ksr-tag-not-set-manual.phpt new file mode 100644 index 00000000000..3487f23fa16 --- /dev/null +++ b/tests/ext/priority_sampling/027-ksr-tag-not-set-manual.phpt @@ -0,0 +1,25 @@ +--TEST-- +_dd.p.ksr propagated tag is NOT set for manual sampling +--ENV-- +DD_TRACE_SAMPLE_RATE=1 +DD_TRACE_GENERATE_ROOT_SPAN=1 +--FILE-- +meta["manual.keep"] = true; + +\DDTrace\get_priority_sampling(); + +if (!isset($root->metrics["_dd.rule_psr"])) { + echo "No rule_psr OK\n"; +} else { + echo "rule_psr unexpectedly set\n"; +} + +echo "_dd.p.ksr = ", isset($root->meta["_dd.p.ksr"]) ? $root->meta["_dd.p.ksr"] : "-", "\n"; +echo "_dd.p.dm = {$root->meta["_dd.p.dm"]}\n"; +?> +--EXPECT-- +No rule_psr OK +_dd.p.ksr = - +_dd.p.dm = -4 diff --git a/tests/ext/priority_sampling/028-ksr-tag-formatting.phpt b/tests/ext/priority_sampling/028-ksr-tag-formatting.phpt new file mode 100644 index 00000000000..6d102eec09e --- /dev/null +++ b/tests/ext/priority_sampling/028-ksr-tag-formatting.phpt @@ -0,0 +1,18 @@ +--TEST-- +_dd.p.ksr propagated tag formats rate with up to 6 significant digits and no trailing zeros +--ENV-- +DD_TRACE_SAMPLING_RULES=[{"sample_rate": 0.7654321}] +DD_TRACE_GENERATE_ROOT_SPAN=1 +--FILE-- +meta["_dd.p.ksr"]) ? $root->meta["_dd.p.ksr"] : "-", "\n"; +// Verify it's a string in meta, not metrics +echo "is_string = ", is_string($root->meta["_dd.p.ksr"] ?? null) ? "true" : "false", "\n"; +?> +--EXPECTREGEX-- +_dd.p.ksr = 0.765432 +is_string = true From 7821296dd08c254c954516b90699924f63da94a4 Mon Sep 17 00:00:00 2001 From: bm1549 Date: Tue, 10 Mar 2026 22:22:21 -0400 Subject: [PATCH 2/5] fix: only set _dd.p.ksr for explicit agent rates, not default mechanism The default sampling mechanism (DD_MECHANISM_DEFAULT) is not an agent rate. Per the RFC, _dd.p.ksr should only be set for "Agent Sampling rate or Trace Sampling rules". This prevents _dd.p.ksr from appearing on every span when no agent rates are configured. Co-Authored-By: Claude Opus 4.6 --- ext/priority_sampling/priority_sampling.c | 4 +++- .../ext/priority_sampling/026-ksr-tag-default-sampling.phpt | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ext/priority_sampling/priority_sampling.c b/ext/priority_sampling/priority_sampling.c index a12953f6416..eb17721d91c 100644 --- a/ext/priority_sampling/priority_sampling.c +++ b/ext/priority_sampling/priority_sampling.c @@ -343,7 +343,9 @@ static void dd_decide_on_sampling(ddtrace_root_span_data *span) { priority = sampling && !limited ? PRIORITY_SAMPLING_AUTO_KEEP : PRIORITY_SAMPLING_AUTO_REJECT; zend_hash_str_update(metrics, ZEND_STRL("_dd.agent_psr"), &sample_rate_zv); - dd_update_knuth_sampling_rate_tag(span, sample_rate); + if (mechanism == DD_MECHANISM_AGENT_RATE) { + dd_update_knuth_sampling_rate_tag(span, sample_rate); + } } if (limited) { diff --git a/tests/ext/priority_sampling/026-ksr-tag-default-sampling.phpt b/tests/ext/priority_sampling/026-ksr-tag-default-sampling.phpt index 4303e2546f0..e37f5913060 100644 --- a/tests/ext/priority_sampling/026-ksr-tag-default-sampling.phpt +++ b/tests/ext/priority_sampling/026-ksr-tag-default-sampling.phpt @@ -1,5 +1,5 @@ --TEST-- -_dd.p.ksr propagated tag is set for default/agent-based sampling +_dd.p.ksr propagated tag is NOT set for default sampling (only for explicit agent rates) --ENV-- DD_TRACE_GENERATE_ROOT_SPAN=1 --FILE-- @@ -14,10 +14,10 @@ if ($root->metrics["_dd.agent_psr"] === 1.0) { echo "Agent PSR missing\n"; } -echo "_dd.p.ksr = ", isset($root->meta["_dd.p.ksr"]) ? $root->meta["_dd.p.ksr"] : "-", "\n"; +echo "_dd.p.ksr = ", isset($root->meta["_dd.p.ksr"]) ? $root->meta["_dd.p.ksr"] : "not set", "\n"; echo "_dd.p.dm = {$root->meta["_dd.p.dm"]}\n"; ?> --EXPECT-- Agent PSR OK -_dd.p.ksr = 1 +_dd.p.ksr = not set _dd.p.dm = -0 From ce49c53a3257d9d6d7e1694e0d7043a60df5f704 Mon Sep 17 00:00:00 2001 From: bm1549 Date: Tue, 10 Mar 2026 22:35:31 -0400 Subject: [PATCH 3/5] fix: add _dd.p.ksr to expected output in sampling_rules.phpt The inferred proxy sampling rules test uses explicit DD_TRACE_SAMPLING_RULES which triggers rule-based sampling. The ksr tag now appears in span meta. Co-Authored-By: Claude Opus 4.6 --- tests/ext/inferred_proxy/sampling_rules.phpt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ext/inferred_proxy/sampling_rules.phpt b/tests/ext/inferred_proxy/sampling_rules.phpt index 2dfb9628ded..eec6a5a1092 100644 --- a/tests/ext/inferred_proxy/sampling_rules.phpt +++ b/tests/ext/inferred_proxy/sampling_rules.phpt @@ -48,6 +48,7 @@ echo json_encode(dd_trace_serialize_closed_spans(), JSON_PRETTY_PRINT); "service": "foo", "type": "web", "meta": { + "_dd.p.ksr": "0.3", "http.method": "GET", "http.status_code": "200", "http.url": "http:\/\/localhost:8888\/foo", @@ -71,6 +72,7 @@ echo json_encode(dd_trace_serialize_closed_spans(), JSON_PRETTY_PRINT); "type": "web", "meta": { "_dd.p.dm": "-3", + "_dd.p.ksr": "0.3", "_dd.p.tid": "%s", "component": "aws-apigateway", "http.method": "GET", From d3d2938726123365b5df005a746c39f44157e587 Mon Sep 17 00:00:00 2001 From: bm1549 Date: Tue, 10 Mar 2026 22:44:48 -0400 Subject: [PATCH 4/5] fix: propagate _dd.p.ksr to inferred span and ignore in SpanChecker Two fixes for remaining CI failures after the initial KSR tag implementation: 1. serializer.c: Copy _dd.p.ksr from the root span to the inferred proxy span during serialization (using false to keep it on the root span too), matching the behavior expected by sampling_rules.phpt. 2. SpanChecker.php: Auto-ignore _dd.p.ksr in withExactTags() assertions unless the test explicitly tests for it, consistent with how _dd.p.dm and _dd.p.tid are handled. This fixes ResponseStatusCodeTest and other integration tests that don't test KSR behavior directly. Co-Authored-By: Claude Sonnet 4.6 --- ext/serializer.c | 1 + tests/Common/SpanChecker.php | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/ext/serializer.c b/ext/serializer.c index 108b458a080..03738e0e733 100644 --- a/ext/serializer.c +++ b/ext/serializer.c @@ -1906,6 +1906,7 @@ ddog_SpanBytes *ddtrace_serialize_span_to_rust_span(ddtrace_span_data *span, ddo transfer_meta_data(rust_span, serialized_inferred_span, "error.stack", false); transfer_meta_data(rust_span, serialized_inferred_span, "track_error", false); transfer_meta_data(rust_span, serialized_inferred_span, "_dd.p.dm", true); + transfer_meta_data(rust_span, serialized_inferred_span, "_dd.p.ksr", false); transfer_meta_data(rust_span, serialized_inferred_span, "_dd.p.tid", true); ddog_set_span_error(serialized_inferred_span, ddog_get_span_error(rust_span)); diff --git a/tests/Common/SpanChecker.php b/tests/Common/SpanChecker.php index 47482ee0013..6c7552f34d4 100644 --- a/tests/Common/SpanChecker.php +++ b/tests/Common/SpanChecker.php @@ -478,6 +478,10 @@ function ($key) use ($pattern) { if (!isset($expectedTags['_dd.p.dm'])) { unset($filtered['_dd.p.dm']); } + // Ignore _dd.p.ksr unless explicitly tested + if (!isset($expectedTags['_dd.p.ksr'])) { + unset($filtered['_dd.p.ksr']); + } // Ignore _dd.p.tid unless explicitly tested if (!isset($expectedTags['_dd.p.tid'])) { unset($filtered['_dd.p.tid']); From db13664747c969c6f210bdfea8a21574d57c3bcf Mon Sep 17 00:00:00 2001 From: bm1549 Date: Wed, 11 Mar 2026 00:15:25 -0400 Subject: [PATCH 5/5] perf: skip ksr tag update when value is unchanged In the hot sampling path, avoid re-allocating strings and updating hash tables when _dd.p.ksr already contains the same value. This optimization recovers most of the performance regression seen in SamplingRuleMatchingBench when sampling is re-evaluated with the same rate (the common case for long-running spans). Co-Authored-By: Claude Sonnet 4.6 --- ext/priority_sampling/priority_sampling.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ext/priority_sampling/priority_sampling.c b/ext/priority_sampling/priority_sampling.c index eb17721d91c..1e6c55179d4 100644 --- a/ext/priority_sampling/priority_sampling.c +++ b/ext/priority_sampling/priority_sampling.c @@ -69,6 +69,12 @@ static void dd_update_knuth_sampling_rate_tag(ddtrace_root_span_data *root_span, char buf[32]; snprintf(buf, sizeof(buf), "%.6g", sample_rate); + // Skip update if already set to the same value + zval *existing = zend_hash_str_find(meta, ZEND_STRL("_dd.p.ksr")); + if (existing && Z_TYPE_P(existing) == IS_STRING && strcmp(Z_STRVAL_P(existing), buf) == 0) { + return; + } + zval ksr; ZVAL_STRING(&ksr, buf); zend_hash_str_update(meta, ZEND_STRL("_dd.p.ksr"), &ksr);