diff --git a/ext/priority_sampling/priority_sampling.c b/ext/priority_sampling/priority_sampling.c index 57f845fe27f..1e6c55179d4 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,24 @@ 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); + + // 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); + 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 +339,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 +349,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); + if (mechanism == DD_MECHANISM_AGENT_RATE) { + dd_update_knuth_sampling_rate_tag(span, sample_rate); + } } if (limited) { 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']); 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", 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..e37f5913060 --- /dev/null +++ b/tests/ext/priority_sampling/026-ksr-tag-default-sampling.phpt @@ -0,0 +1,23 @@ +--TEST-- +_dd.p.ksr propagated tag is NOT set for default sampling (only for explicit agent rates) +--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"] : "not set", "\n"; +echo "_dd.p.dm = {$root->meta["_dd.p.dm"]}\n"; +?> +--EXPECT-- +Agent PSR OK +_dd.p.ksr = not set +_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