Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d2104b0
feat(propagation): model otel probability sampling state
MilanGarnier Sep 14, 2026
7025ae9
refactor(propagation): introduce immutable sampling snapshots
MilanGarnier Sep 14, 2026
1c8a762
feat(sampling): publish probability decisions atomically
MilanGarnier Sep 14, 2026
e470bfc
feat(propagation): inject consistent otel sampling state
MilanGarnier Sep 14, 2026
27cc740
feat(otlp): export consistent otel sampling state
MilanGarnier Sep 14, 2026
a5a43ee
perf(propagation): reduce sampling state allocations
MilanGarnier Sep 14, 2026
b5b84b2
fix(propagation): preserve locked decision maker
MilanGarnier Sep 14, 2026
de7920c
refactor(propagation): simplify otel sampling state
MilanGarnier Sep 14, 2026
d324e16
fix(propagation): reset otel state for Org Guard resampling
MilanGarnier Sep 15, 2026
a51831b
update test values across tests which were hardcoding the tracestate …
MilanGarnier Sep 15, 2026
255fcb2
remove fast path for OTel rebuildTracestate as it requires checking no
MilanGarnier Sep 15, 2026
ac097ca
fix(ptags): use private lock for sampling updates
MilanGarnier Sep 15, 2026
10af999
test(otlp): normalize inherited tracestate assertions
MilanGarnier Sep 16, 2026
efcf28a
fix(sampling): serialize rate only on root spans
MilanGarnier Sep 16, 2026
af7e307
pass the override info in the samplingMechanism instead of passing th…
MilanGarnier Sep 22, 2026
88f374c
refactor(propagation): remove redundant codec overrides
MilanGarnier Sep 22, 2026
2fe4d46
fix(sampling): preserve negative mechanisms
MilanGarnier Sep 22, 2026
3728627
Small simplifications/re-use
mcculls Sep 23, 2026
cbf8c32
Move RATE_LIMITER_REJECTED back to being a boolean parameter, but use…
mcculls Sep 23, 2026
90e53db
Merge remote-tracking branch 'origin/master' into milan.garnier/ot.th
mcculls Sep 23, 2026
1a0f06f
Update tryUpdateProbabilitySamplingDecision parameter order
mcculls Sep 23, 2026
b4a6c30
fix(propagation): reconcile unknown otel state
MilanGarnier Sep 24, 2026
b1022f8
perf(propagation): reuse unchanged sampling state
MilanGarnier Sep 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ class OpenTelemetryTest extends InstrumentationSpecification {
}
if (contextPriority == UNSET) {
expectedTracestate += ";t.ksr:1"
expectedTracestate += ",ot=${span.delegate.spanContext().propagationTags.samplingState().otelTraceState}"
}
if (traceId.toHighOrderLong() != 0) {
expectedDataTags << "_dd.p.tid=" + traceId.toHexStringPadded(32).substring(0, 16)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ class OpenTracing31Test extends InstrumentationSpecification {
}
if (contextPriority == UNSET) {
expectedTracestate += ";t.ksr:1"
expectedTracestate += ",ot=${context.delegate.propagationTags.samplingState().otelTraceState}"
datadogTags << "_dd.p.ksr=1"
}
def expectedTextMap = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ class OpenTracing32Test extends InstrumentationSpecification {
}
if (contextPriority == UNSET) {
expectedTracestate+= ";t.ksr:1"
expectedTracestate+= ",ot=${context.delegate.propagationTags.samplingState().otelTraceState}"
datadogTags << "_dd.p.ksr=1"
}
def expectedTextMap = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,13 @@ public <T extends CoreSpan<T>> void setSamplingPriority(final T span) {
final RateSamplersByEnvAndService rates = serviceRates;
RateSampler sampler = rates.getSampler(env, serviceName);

if (sampler.sample(span)) {
span.setSamplingPriority(
PrioritySampling.SAMPLER_KEEP,
SAMPLING_AGENT_RATE,
sampler.getSampleRate(),
SamplingMechanism.AGENT_RATE);
} else {
span.setSamplingPriority(
PrioritySampling.SAMPLER_DROP,
SAMPLING_AGENT_RATE,
sampler.getSampleRate(),
SamplingMechanism.AGENT_RATE);
}
boolean sampled = sampler.sample(span);
int samplingPriority = sampled ? PrioritySampling.SAMPLER_KEEP : PrioritySampling.SAMPLER_DROP;
span.setSamplingPriority(
samplingPriority,
SAMPLING_AGENT_RATE,
sampler.getSampleRate(),
SamplingMechanism.AGENT_RATE);
}

private <T extends CoreSpan<T>> String getSpanEnv(final T span) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ public <T extends CoreSpan<T>> void setSamplingPriority(final T span) {
PrioritySampling.USER_DROP,
SAMPLING_RULE_RATE,
matchedRule.getSampler().getSampleRate(),
matchedRule.getMechanism());
matchedRule.getMechanism(),
true);
}
span.setMetric(SAMPLING_LIMIT_RATE, rateLimit);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,15 @@ default void processTagsAndBaggageWithStructuredLinks(
T setSamplingPriority(
int samplingPriority, CharSequence rate, double sampleRate, int samplingMechanism);

default T setSamplingPriority(
int samplingPriority,
CharSequence rate,
double sampleRate,
int samplingMechanism,
boolean rateLimiterRejected) {
return setSamplingPriority(samplingPriority, rate, sampleRate, samplingMechanism);
}

T setSpanSamplingPriority(double rate, int limit);

T setMetric(CharSequence name, int value);
Expand Down
23 changes: 16 additions & 7 deletions dd-trace-core/src/main/java/datadog/trace/core/DDSpan.java
Original file line number Diff line number Diff line change
Expand Up @@ -648,14 +648,23 @@ public final DDSpan setSamplingPriority(final int newPriority, int samplingMecha
@Override
public DDSpan setSamplingPriority(
int samplingPriority, CharSequence rate, double sampleRate, int samplingMechanism) {
if (context.setSamplingPriority(samplingPriority, samplingMechanism)) {
return setSamplingPriority(samplingPriority, rate, sampleRate, samplingMechanism, false);
}

@Override
public DDSpan setSamplingPriority(
int samplingPriority,
CharSequence rate,
double sampleRate,
int samplingMechanism,
boolean rateLimiterRejected) {
if (context.setSamplingPriority(
samplingPriority,
samplingMechanism,
sampleRate,
getTraceId().toLong(),
rateLimiterRejected)) {
setMetric(rate, sampleRate);
if (samplingMechanism == SamplingMechanism.AGENT_RATE
|| samplingMechanism == SamplingMechanism.LOCAL_USER_RULE
|| samplingMechanism == SamplingMechanism.REMOTE_USER_RULE
|| samplingMechanism == SamplingMechanism.REMOTE_ADAPTIVE_RULE) {
context.getPropagationTags().updateKnuthSamplingRate(sampleRate);
}
}
return this;
}
Expand Down
58 changes: 34 additions & 24 deletions dd-trace-core/src/main/java/datadog/trace/core/DDSpanContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.function.Function;
import javax.annotation.Nonnull;
import org.slf4j.Logger;
Expand Down Expand Up @@ -161,11 +160,6 @@ public class DDSpanContext

private volatile boolean topLevel;

private static final AtomicIntegerFieldUpdater<DDSpanContext> SAMPLING_PRIORITY_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(DDSpanContext.class, "samplingPriority");

private volatile int samplingPriority = PrioritySampling.UNSET;

/** The origin of the trace. (eg. Synthetics, CI App) */
private volatile CharSequence origin;

Expand Down Expand Up @@ -667,10 +661,6 @@ public void forceKeep(byte samplingMechanism) {
}

private void forceKeepThisSpan(byte samplingMechanism) {
// if the user really wants to keep this trace chunk, we will let them,
// even if the old sampling priority and mechanism have already propagated
SAMPLING_PRIORITY_UPDATER.set(this, PrioritySampling.USER_KEEP);
// record force keep decision for future distributed trace propagation
propagationTags.forceKeep(samplingMechanism);
}

Expand Down Expand Up @@ -710,26 +700,43 @@ private boolean setThisSpanSamplingPriority(final int newPriority, final int new
if (!validateSamplingPriority(newPriority, newMechanism)) {
return false;
}
if (SamplingMechanism.canAvoidSamplingPriorityLock(newPriority, newMechanism)) {
SAMPLING_PRIORITY_UPDATER.set(this, newPriority);
propagationTags.updateTraceSamplingPriority(newPriority, newMechanism);
return true;
}
if (!SAMPLING_PRIORITY_UPDATER.compareAndSet(this, PrioritySampling.UNSET, newPriority)) {
boolean updated =
propagationTags.tryUpdateTraceSamplingPriority(
newPriority,
newMechanism,
SamplingMechanism.canAvoidSamplingPriorityLock(newPriority, newMechanism));
if (!updated) {
if (log.isDebugEnabled()) {
log.debug(
"samplingPriority locked at priority: {}. Refusing to set to priority: {} mechanism: {}",
samplingPriority,
propagationTags.getSamplingPriority(),
newPriority,
newMechanism);
}
return false;
}
// set trace level sampling priority tag propagationTags
propagationTags.updateTraceSamplingPriority(newPriority, newMechanism);
return true;
}

public boolean setSamplingPriority(
final int newPriority,
final int newMechanism,
final double sampleRate,
final long traceIdLowOrderBits,
final boolean rateLimiterRejected) {
DDSpanContext spanContext = getRootSpanContextOrThis();
if (!spanContext.validateSamplingPriority(newPriority, newMechanism)) {
return false;
}
return spanContext.propagationTags.tryUpdateProbabilitySamplingDecision(
newPriority,
newMechanism,
sampleRate,
rateLimiterRejected,
traceIdLowOrderBits,
SamplingMechanism.canAvoidSamplingPriorityLock(newPriority, newMechanism));
}

private boolean validateSamplingPriority(final int newPriority, final int newMechanism) {
if (newPriority == PrioritySampling.UNSET) {
log.debug("{}: Refusing to set samplingPriority to UNSET", this);
Expand Down Expand Up @@ -758,7 +765,7 @@ private boolean validateSamplingPriority(final int newPriority, final int newMec

@Override
public int getSamplingPriority() {
return getRootSpanContextOrThis().samplingPriority;
return getRootSpanContextOrThis().propagationTags.getSamplingPriority();
}

public void setSpanSamplingPriority(double rate, int limit) {
Expand Down Expand Up @@ -790,7 +797,7 @@ public boolean lockSamplingPriority() {
return rootSpan.spanContext().lockSamplingPriority();
}

return SAMPLING_PRIORITY_UPDATER.get(this) != PrioritySampling.UNSET;
return propagationTags.getSamplingPriority() != PrioritySampling.UNSET;
}

public CharSequence getOrigin() {
Expand Down Expand Up @@ -1258,8 +1265,11 @@ public TagMap getTags() {
tags.put(DDTags.THREAD_ID, threadId);
// maintain previously observable type of the thread name :|
tags.put(DDTags.THREAD_NAME, threadName.toString());
if (samplingPriority != PrioritySampling.UNSET) {
tags.put(SAMPLE_RATE_KEY, samplingPriority);
int currentSamplingPriority = getSamplingPriority();
// add _sample_rate tag only on the root/local span owning the decision
if (getRootSpanContextIfDifferent() == null
&& currentSamplingPriority != PrioritySampling.UNSET) {
tags.put(SAMPLE_RATE_KEY, currentSamplingPriority);
}
if (httpStatusCode != 0) {
tags.put(Tags.HTTP_STATUS, (int) httpStatusCode);
Expand Down Expand Up @@ -1408,7 +1418,7 @@ void processTagsAndBaggage(
threadName,
unsafeTags,
baggageItemsWithPropagationTags,
samplingPriority != PrioritySampling.UNSET ? samplingPriority : getSamplingPriority(),
getSamplingPriority(),
measured,
topLevel,
httpStatusCode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import datadog.trace.core.MetadataConsumer;
import datadog.trace.core.PendingTrace;
import datadog.trace.core.propagation.PropagationTags;
import datadog.trace.core.propagation.PropagationTags.SamplingState;
import java.util.List;
import java.util.Map;

Expand All @@ -51,13 +52,14 @@ private OtlpTraceJson() {}
public static void writeSpan(
JsonWriter writer, DDSpan span, MetaWriter metaWriter, List<? extends AgentSpanLink> links) {
PropagationTags propagationTags = span.spanContext().getPropagationTags();
SamplingState samplingState = propagationTags.samplingState();

writer.beginObject();

writer.name("traceId").value(hexTraceId(span.getTraceId()));
writer.name("spanId").value(hexSpanId(span.getSpanId()));

String tracestate = propagationTags.getW3CTracestate();
String tracestate = propagationTags.getW3CTracestate(samplingState);
if (tracestate != null) {
writer.name("traceState").value(tracestate);
}
Expand All @@ -67,7 +69,7 @@ public static void writeSpan(
}

int traceFlags = NO_TRACE_FLAGS;
if (span.samplingPriority() > 0) {
if (samplingState.getSamplingPriority() > 0) {
traceFlags |= SAMPLED_TRACE_FLAG;
}
if (span.spanContext().isRemote()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import datadog.trace.core.PendingTrace;
import datadog.trace.core.otlp.common.OtlpProtoBuffer;
import datadog.trace.core.propagation.PropagationTags;
import datadog.trace.core.propagation.PropagationTags.SamplingState;

/** Provides optimized writers for OpenTelemetry's "trace.proto" wire protocol. */
public final class OtlpTraceProto {
Expand Down Expand Up @@ -84,14 +85,15 @@ public static int recordSpanMessage(
int nestedSpanLinkBytes,
OtlpProtoBuffer protobuf) {
PropagationTags propagationTags = span.spanContext().getPropagationTags();
SamplingState samplingState = propagationTags.samplingState();

writeTag(buf, 1, LEN_WIRE_TYPE);
writeTraceId(buf, span.getTraceId());

writeTag(buf, 2, LEN_WIRE_TYPE);
writeSpanId(buf, span.getSpanId());

String tracestate = propagationTags.getW3CTracestate();
String tracestate = propagationTags.getW3CTracestate(samplingState);
if (tracestate != null) {
writeTag(buf, 3, LEN_WIRE_TYPE);
writeString(buf, tracestate);
Expand All @@ -103,7 +105,7 @@ public static int recordSpanMessage(
}

int traceFlags = NO_TRACE_FLAGS;
if (span.samplingPriority() > 0) {
if (samplingState.getSamplingPriority() > 0) {
traceFlags |= SAMPLED_TRACE_FLAG;
}
if (span.spanContext().isRemote()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,47 @@
*/
public abstract class PropagationTags {

public static final class SamplingState {
private final int samplingPriority;
private final String tracestate;
private final CharSequence otelTraceState;
private final CharSequence decisionMaker;
private final CharSequence knuthSamplingRate;

public SamplingState(
int samplingPriority,
String tracestate,
CharSequence otelTraceState,
CharSequence decisionMaker,
CharSequence knuthSamplingRate) {
this.samplingPriority = samplingPriority;
this.tracestate = tracestate;
this.otelTraceState = otelTraceState;
this.decisionMaker = decisionMaker;
this.knuthSamplingRate = knuthSamplingRate;
}

public int getSamplingPriority() {
return samplingPriority;
}

public String getTracestate() {
return tracestate;
}

public CharSequence getOtelTraceState() {
return otelTraceState;
}

public CharSequence getDecisionMaker() {
return decisionMaker;
}

public CharSequence getKnuthSamplingRate() {
return knuthSamplingRate;
}
}

public static PropagationTags.Factory factory(Config config) {
return factory(config.getxDatadogTagsMaxLength());
}
Expand Down Expand Up @@ -65,10 +106,23 @@ public interface Factory {
*/
public abstract void updateTraceSamplingPriority(int samplingPriority, int samplingMechanism);

public abstract boolean tryUpdateTraceSamplingPriority(
int samplingPriority, int samplingMechanism, boolean allowOverride);

public abstract boolean tryUpdateProbabilitySamplingDecision(
int samplingPriority,
int samplingMechanism,
double sampleRate,
boolean rateLimiterRejected,
long traceIdLowOrderBits,
boolean allowOverride);

public abstract void forceKeep(int samplingMechanism);

public abstract int getSamplingPriority();

public abstract SamplingState samplingState();

public abstract void updateTraceOrigin(CharSequence origin);

public abstract CharSequence getOrigin();
Expand All @@ -87,6 +141,8 @@ public interface Factory {
*/
public abstract String getW3CTracestate();

public abstract String getW3CTracestate(SamplingState samplingState);

/**
* Stores the original <a href="https://www.w3.org/TR/trace-context/#tracestate-header">W3C
* tracestate header</a> value.
Expand Down Expand Up @@ -116,6 +172,9 @@ public void updateW3CTracestateFrom(PropagationTags source) {
*/
public abstract String headerValue(HeaderType headerType, CharSequence lastParentIdOverride);

public abstract String headerValue(
HeaderType headerType, CharSequence lastParentIdOverride, SamplingState samplingState);

/**
* Fills a provided tagMap with valid propagated _dd.p.* tags and possibly a new sampling decision
* tags _dd.p.dm (root span only) based on the current state, or sets only an error tag if the
Expand Down
Loading
Loading