Skip to content

Commit cb7930d

Browse files
committed
fix: ignore deferred spans when delegating sampling decision
1 parent d721037 commit cb7930d

File tree

2 files changed

+38
-6
lines changed

2 files changed

+38
-6
lines changed

opentelemetry-sdk/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## vNext
44

5+
- **Fix**: `Sampler::ParentBased` now correctly delegates sampling decisions to the `delegate_sampler` when no sampling information was extracted by the context propagator. Previously `SamplingDecision::Drop` was returned in such cases. ([#3209](https://github.com/open-telemetry/opentelemetry-rust/pull/3209)).
6+
57
## 0.31.0
68

79
Released 2025-Sep-25

opentelemetry-sdk/src/trace/sampler.rs

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use opentelemetry::{
22
trace::{
33
Link, SamplingDecision, SamplingResult, SpanKind, TraceContextExt, TraceId, TraceState,
44
},
5-
Context, KeyValue,
5+
Context, KeyValue, TraceFlags,
66
};
77

88
#[cfg(feature = "jaeger_remote_sampler")]
@@ -13,6 +13,8 @@ pub use jaeger_remote::{JaegerRemoteSampler, JaegerRemoteSamplerBuilder};
1313
#[cfg(feature = "jaeger_remote_sampler")]
1414
use opentelemetry_http::HttpClient;
1515

16+
const TRACE_FLAG_DEFERRED: TraceFlags = TraceFlags::new(0x02);
17+
1618
/// The [`ShouldSample`] interface allows implementations to provide samplers
1719
/// which will return a sampling [`SamplingResult`] based on information that
1820
/// is typically available just before the [`Span`] was created.
@@ -180,6 +182,12 @@ impl ShouldSample for Sampler {
180182
// The parent decision if sampled; otherwise the decision of delegate_sampler
181183
Sampler::ParentBased(delegate_sampler) => parent_context
182184
.filter(|cx| cx.has_active_span())
185+
.map(|cx| cx.span())
186+
.filter(|span| {
187+
let trace_flags = span.span_context().trace_flags();
188+
let is_deferred = trace_flags & TRACE_FLAG_DEFERRED == TRACE_FLAG_DEFERRED;
189+
!is_deferred || trace_flags.is_sampled()
190+
})
183191
.map_or_else(
184192
|| {
185193
delegate_sampler
@@ -193,10 +201,8 @@ impl ShouldSample for Sampler {
193201
)
194202
.decision
195203
},
196-
|ctx| {
197-
let span = ctx.span();
198-
let parent_span_context = span.span_context();
199-
if parent_span_context.is_sampled() {
204+
|span| {
205+
if span.span_context().is_sampled() {
200206
SamplingDecision::RecordAndSample
201207
} else {
202208
SamplingDecision::Drop
@@ -249,7 +255,7 @@ pub(crate) fn sample_based_on_probability(prob: &f64, trace_id: TraceId) -> Samp
249255
mod tests {
250256
use super::*;
251257
use crate::testing::trace::TestSpan;
252-
use opentelemetry::trace::{SpanContext, SpanId, TraceFlags};
258+
use opentelemetry::trace::{SpanContext, SpanId};
253259
use rand::random;
254260

255261
#[rustfmt::skip]
@@ -419,6 +425,30 @@ mod tests {
419425
))),
420426
SamplingDecision::RecordAndSample,
421427
),
428+
(
429+
"should ignore deferred spans",
430+
Sampler::AlwaysOn,
431+
Context::current_with_span(TestSpan(SpanContext::new(
432+
TraceId::from(1),
433+
SpanId::from(1),
434+
TRACE_FLAG_DEFERRED, // deferred
435+
false,
436+
TraceState::default(),
437+
))),
438+
SamplingDecision::RecordAndSample,
439+
),
440+
(
441+
"should prioritize sampled flag over deferred",
442+
Sampler::AlwaysOff,
443+
Context::current_with_span(TestSpan(SpanContext::new(
444+
TraceId::from(1),
445+
SpanId::from(1),
446+
TraceFlags::SAMPLED | TRACE_FLAG_DEFERRED, // sampled and deferred (invalid state)
447+
false,
448+
TraceState::default(),
449+
))),
450+
SamplingDecision::RecordAndSample,
451+
),
422452
];
423453

424454
for (name, delegate, parent_cx, expected) in test_cases {

0 commit comments

Comments
 (0)