Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -10,7 +10,6 @@
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.trace.data.LinkData;
import java.util.List;
import java.util.function.Function;

enum ComposableAlwaysOnSampler implements ComposableSampler {
INSTANCE;
Expand All @@ -20,7 +19,7 @@ enum ComposableAlwaysOnSampler implements ComposableSampler {
ImmutableSamplingIntent.MIN_THRESHOLD,
/* adjustedCountReliable= */ true,
Attributes.empty(),
Function.identity());
ImmutableSamplingIntent.NO_TRACE_STATE_UPDATE);

@Override
public SamplingIntent getSamplingIntent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import static io.opentelemetry.sdk.extension.incubator.trace.samplers.ImmutableSamplingIntent.INVALID_THRESHOLD;
import static io.opentelemetry.sdk.extension.incubator.trace.samplers.ImmutableSamplingIntent.MIN_THRESHOLD;
import static io.opentelemetry.sdk.extension.incubator.trace.samplers.ImmutableSamplingIntent.NO_TRACE_STATE_UPDATE;
import static io.opentelemetry.sdk.extension.incubator.trace.samplers.ImmutableSamplingIntent.isValidThreshold;

import io.opentelemetry.api.common.Attributes;
Expand All @@ -16,7 +17,6 @@
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.trace.data.LinkData;
import java.util.List;
import java.util.function.Function;

final class ComposableParentThresholdSampler implements ComposableSampler {

Expand All @@ -40,19 +40,23 @@ public SamplingIntent getSamplingIntent(
return rootSampler.getSamplingIntent(parentContext, name, spanKind, attributes, parentLinks);
}

boolean parentSampled = parentSpanContext.getTraceFlags().isSampled();
OtelTraceState otTraceState = OtelTraceState.parse(parentSpanContext.getTraceState());
if (isValidThreshold(otTraceState.getThreshold())) {
long parentThreshold = otTraceState.getThreshold();
if (isValidThreshold(parentThreshold)
&& (!parentSampled
|| parentThreshold
<= otTraceState.getRandomValueOrFromTraceId(parentSpanContext.getTraceId()))) {
return ImmutableSamplingIntent.create(
otTraceState.getThreshold(),
parentThreshold,
/* adjustedCountReliable= */ true,
Attributes.empty(),
Function.identity());
NO_TRACE_STATE_UPDATE);
}

long threshold =
parentSpanContext.getTraceFlags().isSampled() ? MIN_THRESHOLD : INVALID_THRESHOLD;
long threshold = parentSampled ? MIN_THRESHOLD : INVALID_THRESHOLD;
return ImmutableSamplingIntent.create(
threshold, /* adjustedCountReliable= */ false, Attributes.empty(), Function.identity());
threshold, /* adjustedCountReliable= */ false, Attributes.empty(), NO_TRACE_STATE_UPDATE);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.trace.data.LinkData;
import java.util.List;
import java.util.function.Function;

final class ComposableProbabilitySampler implements ComposableSampler {
private static long calculateThreshold(double ratio) {
Expand All @@ -35,7 +34,7 @@ private static long calculateThreshold(double ratio) {
ImmutableSamplingIntent.INVALID_THRESHOLD,
/* adjustedCountReliable= */ false,
Attributes.empty(),
Function.identity());
ImmutableSamplingIntent.NO_TRACE_STATE_UPDATE);
} else {
StringBuilder sb = new StringBuilder();
OtelTraceState.serializeTh(threshold, sb);
Expand All @@ -46,7 +45,7 @@ private static long calculateThreshold(double ratio) {
threshold,
/* adjustedCountReliable= */ true,
Attributes.empty(),
Function.identity());
ImmutableSamplingIntent.NO_TRACE_STATE_UPDATE);
}
this.description =
"ComposableProbabilitySampler{threshold=" + thresholdStr + ", ratio=" + ratio + "}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@
package io.opentelemetry.sdk.extension.incubator.trace.samplers;

import static io.opentelemetry.sdk.extension.incubator.trace.samplers.ImmutableSamplingIntent.INVALID_THRESHOLD;
import static io.opentelemetry.sdk.extension.incubator.trace.samplers.ImmutableSamplingIntent.isValidRandomValue;
import static io.opentelemetry.sdk.extension.incubator.trace.samplers.ImmutableSamplingIntent.isValidThreshold;
import static io.opentelemetry.sdk.extension.incubator.trace.samplers.OtelTraceState.OTEL_TRACE_STATE_KEY;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.internal.OtelEncodingUtils;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.TraceState;
Expand Down Expand Up @@ -61,12 +59,7 @@ public SamplingResult shouldSample(
adjustedCountReliable = intent.isAdjustedCountReliable();
long randomValue;
if (adjustedCountReliable) {
if (isValidRandomValue(otelTraceState.getRandomValue())) {
randomValue = otelTraceState.getRandomValue();
} else {
// Use last 56 bits of trace ID as random value.
randomValue = OtelEncodingUtils.longFromBase16String(traceId, 16) & 0x00FFFFFFFFFFFFFFL;
}
randomValue = otelTraceState.getRandomValueOrFromTraceId(traceId);
} else {
randomValue = RandomSupplier.platformDefault().get().nextLong() & 0x00FFFFFFFFFFFFFFL;
}
Expand Down Expand Up @@ -99,10 +92,12 @@ public Attributes getAttributes() {

@Override
public TraceState getUpdatedTraceState(TraceState parentTraceState) {
TraceState newTraceState = intent.getTraceStateUpdater().apply(traceState);
TraceState newTraceState = intent.getTraceStateUpdater().apply(traceState, decision);
if (!serializedState.isEmpty()) {
newTraceState =
newTraceState.toBuilder().put(OTEL_TRACE_STATE_KEY, serializedState).build();
} else if (newTraceState.get(OTEL_TRACE_STATE_KEY) != null) {
newTraceState = newTraceState.toBuilder().remove(OTEL_TRACE_STATE_KEY).build();
}
return newTraceState;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
import com.google.auto.value.AutoValue;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.TraceState;
import java.util.function.Function;
import io.opentelemetry.sdk.trace.samplers.SamplingDecision;
import java.util.function.BiFunction;

@AutoValue
abstract class ImmutableSamplingIntent implements SamplingIntent {
Expand All @@ -20,12 +21,15 @@ abstract class ImmutableSamplingIntent implements SamplingIntent {
static final long MAX_THRESHOLD = 1L << RANDOM_VALUE_BITS;
static final long MAX_RANDOM_VALUE = MAX_THRESHOLD - 1;

static final BiFunction<TraceState, SamplingDecision, TraceState> NO_TRACE_STATE_UPDATE =
(traceState, decision) -> traceState;

static final SamplingIntent NON_SAMPLING_INTENT =
create(
ImmutableSamplingIntent.INVALID_THRESHOLD,
/* adjustedCountReliable= */ false,
Attributes.empty(),
Function.identity());
NO_TRACE_STATE_UPDATE);

static boolean isValidThreshold(long threshold) {
return threshold >= MIN_THRESHOLD && threshold <= MAX_THRESHOLD;
Expand All @@ -39,7 +43,7 @@ static ImmutableSamplingIntent create(
long threshold,
boolean adjustedCountReliable,
Attributes attributes,
Function<TraceState, TraceState> traceStateUpdater) {
BiFunction<TraceState, SamplingDecision, TraceState> traceStateUpdater) {
return new AutoValue_ImmutableSamplingIntent(
threshold, adjustedCountReliable, attributes, traceStateUpdater);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import static io.opentelemetry.sdk.extension.incubator.trace.samplers.ImmutableSamplingIntent.INVALID_RANDOM_VALUE;
import static io.opentelemetry.sdk.extension.incubator.trace.samplers.ImmutableSamplingIntent.INVALID_THRESHOLD;
import static io.opentelemetry.sdk.extension.incubator.trace.samplers.ImmutableSamplingIntent.MAX_RANDOM_VALUE;
import static io.opentelemetry.sdk.extension.incubator.trace.samplers.ImmutableSamplingIntent.MAX_THRESHOLD;
import static io.opentelemetry.sdk.extension.incubator.trace.samplers.ImmutableSamplingIntent.isValidRandomValue;
import static io.opentelemetry.sdk.extension.incubator.trace.samplers.ImmutableSamplingIntent.isValidThreshold;
Expand Down Expand Up @@ -128,6 +129,17 @@ long getRandomValue() {
return randomValue;
}

/**
* Returns the explicit randomness value if present, otherwise the randomness derived from the
* last 56 bits of the trace ID.
*/
long getRandomValueOrFromTraceId(String traceId) {
if (isValidRandomValue(randomValue)) {
return randomValue;
}
return OtelEncodingUtils.longFromBase16String(traceId, 16) & MAX_RANDOM_VALUE;
}

long getThreshold() {
return threshold;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.TraceState;
import java.util.function.Function;
import io.opentelemetry.sdk.trace.samplers.SamplingDecision;
import java.util.function.BiFunction;

/** Information to make a sampling decision. */
public interface SamplingIntent {
Expand All @@ -17,7 +18,7 @@ static SamplingIntent create(
long threshold,
boolean adjustedCountReliable,
Attributes attributes,
Function<TraceState, TraceState> traceStateUpdater) {
BiFunction<TraceState, SamplingDecision, TraceState> traceStateUpdater) {
return ImmutableSamplingIntent.create(
threshold, adjustedCountReliable, attributes, traceStateUpdater);
}
Expand All @@ -40,6 +41,10 @@ static SamplingIntent create(
/** Returns any attributes to add to the span to record the sampling result. */
Attributes getAttributes();

/** Returns a function to apply to the tracestate of the span to possibly update it. */
Function<TraceState, TraceState> getTraceStateUpdater();
/**
* Returns a function to apply to the parent tracestate and final sampling decision to possibly
* update the tracestate of the span. The function must not modify the OpenTelemetry {@code ot}
* entry of the tracestate.
*/
BiFunction<TraceState, SamplingDecision, TraceState> getTraceStateUpdater();
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,29 @@ void rootSpan() {

@Test
void parentWithThreshold() {
SpanContext parentSpanContext =
SpanContext.create(
TraceId.fromLongs(1, 2),
SpanId.fromLong(3),
TraceFlags.getSampled(),
TraceState.builder()
.put("ot", new OtelTraceState(10, 10, Collections.emptyList()).serialize())
.build());
SamplingIntent intent =
ComposableSampler.parentThreshold(ComposableSampler.alwaysOn())
.getSamplingIntent(
Context.root().with(Span.wrap(parentSpanContext)),
"span",
SpanKind.SERVER,
Attributes.empty(),
Collections.emptyList());
assertThat(intent.getThreshold()).isEqualTo(10);
assertThat(intent.isAdjustedCountReliable()).isTrue();
}

@Test
void parentWithInconsistentThresholdSampled() {
// Sampled even though rv < th, so the threshold is erased and the sampled flag is used.
SpanContext parentSpanContext =
SpanContext.create(
TraceId.fromLongs(1, 2),
Expand All @@ -52,16 +75,59 @@ void parentWithThreshold() {
TraceState.builder()
.put("ot", new OtelTraceState(1, 10, Collections.emptyList()).serialize())
.build());
assertThat(
ComposableSampler.parentThreshold(ComposableSampler.alwaysOn())
.getSamplingIntent(
Context.root().with(Span.wrap(parentSpanContext)),
"span",
SpanKind.SERVER,
Attributes.empty(),
Collections.emptyList())
.getThreshold())
.isEqualTo(10);
SamplingIntent intent =
ComposableSampler.parentThreshold(ComposableSampler.alwaysOn())
.getSamplingIntent(
Context.root().with(Span.wrap(parentSpanContext)),
"span",
SpanKind.SERVER,
Attributes.empty(),
Collections.emptyList());
assertThat(intent.getThreshold()).isZero();
assertThat(intent.isAdjustedCountReliable()).isFalse();
}

@Test
void parentWithInconsistentThresholdSampledTraceIdRandomness() {
// No rv, so randomness comes from the trace ID, which is 2 here.
SpanContext parentSpanContext =
SpanContext.create(
TraceId.fromLongs(1, 2),
SpanId.fromLong(3),
TraceFlags.getSampled(),
TraceState.builder().put("ot", "th:8").build());
SamplingIntent intent =
ComposableSampler.parentThreshold(ComposableSampler.alwaysOn())
.getSamplingIntent(
Context.root().with(Span.wrap(parentSpanContext)),
"span",
SpanKind.SERVER,
Attributes.empty(),
Collections.emptyList());
assertThat(intent.getThreshold()).isZero();
assertThat(intent.isAdjustedCountReliable()).isFalse();
}

@Test
void parentWithThresholdNotSampled() {
SpanContext parentSpanContext =
SpanContext.create(
TraceId.fromLongs(1, 2),
SpanId.fromLong(3),
TraceFlags.getDefault(),
TraceState.builder()
.put("ot", new OtelTraceState(1, 10, Collections.emptyList()).serialize())
.build());
SamplingIntent intent =
ComposableSampler.parentThreshold(ComposableSampler.alwaysOn())
.getSamplingIntent(
Context.root().with(Span.wrap(parentSpanContext)),
"span",
SpanKind.SERVER,
Attributes.empty(),
Collections.emptyList());
assertThat(intent.getThreshold()).isEqualTo(10);
assertThat(intent.isAdjustedCountReliable()).isTrue();
}

@Test
Expand Down
Loading
Loading