diff --git a/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java b/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java index 75513b1b01e..53e9c66905e 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java @@ -2030,7 +2030,9 @@ protected static final DDSpanContext buildSpanContext( requestContextDataIast = null; ciVisibilityContextData = null; } - propagationTags = tracer.propagationTagsFactory.empty(); + // Local children share the parent's PropagationTags (trace-level state; reads route to the + // root) instead of allocating an unused empty() per span. + propagationTags = ddsc.getPropagationTags(); } else { long endToEndStartTime; diff --git a/dd-trace-core/src/test/java/datadog/trace/core/PropagationTagsChildSpanTest.java b/dd-trace-core/src/test/java/datadog/trace/core/PropagationTagsChildSpanTest.java new file mode 100644 index 00000000000..1f20e58c661 --- /dev/null +++ b/dd-trace-core/src/test/java/datadog/trace/core/PropagationTagsChildSpanTest.java @@ -0,0 +1,94 @@ +package datadog.trace.core; + +import static datadog.trace.api.TracePropagationStyle.DATADOG; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import datadog.trace.api.DDTraceId; +import datadog.trace.api.sampling.PrioritySampling; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.core.propagation.ExtractedContext; +import datadog.trace.core.propagation.PropagationTags; +import java.util.Collections; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Regression guard for the "local children share the parent's {@link PropagationTags}" optimization + * in {@code CoreTracer.buildSpanContext}: a non-root (local child) span must still carry the + * inbound distributed {@code _dd.p.*} tags when it injects. + * + *
Inbound {@code _dd.p.*} live on the root's {@code PropagationTags} (inherited from the {@link
+ * ExtractedContext}), and reads route to the root via {@code DDSpanContext.getPropagationTags()} —
+ * so a local child injects the same tags whether it holds its own instance or shares the root's.
+ * That invariant is what lets the child skip allocating its own {@code empty()} and share the
+ * root's instead. If {@link #localChildCarriesInboundDdpTags()} regresses, non-root injection is
+ * dropping inbound {@code _dd.p.*} on every hop.
+ */
+class PropagationTagsChildSpanTest extends DDCoreJavaSpecification {
+
+ private static final String INBOUND_HEADER = "_dd.p.dm=934086a686-4,_dd.p.anytag=value";
+ private static final String INBOUND_TAG = "_dd.p.anytag=value";
+
+ private CoreTracer tracer;
+
+ @BeforeEach
+ void setup() {
+ tracer = tracerBuilder().build();
+ }
+
+ private static ExtractedContext extractedWithDdpTags() {
+ return new ExtractedContext(
+ DDTraceId.ONE,
+ 2,
+ PrioritySampling.SAMPLER_KEEP,
+ null,
+ 0,
+ Collections.