diff --git a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ComposableSampler.java b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/Composable.java similarity index 97% rename from consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ComposableSampler.java rename to consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/Composable.java index 85891f47d..9a4443bb0 100644 --- a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ComposableSampler.java +++ b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/Composable.java @@ -12,7 +12,7 @@ import java.util.List; /** An interface for components to be used by composite consistent probability samplers. */ -public interface ComposableSampler { +public interface Composable { /** * Returns the SamplingIntent that is used for the sampling decision. The SamplingIntent includes diff --git a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentAlwaysOffSampler.java b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentAlwaysOffSampler.java index 2594ef88d..0ab08aef8 100644 --- a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentAlwaysOffSampler.java +++ b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentAlwaysOffSampler.java @@ -33,7 +33,17 @@ public SamplingIntent getSamplingIntent( Attributes attributes, List parentLinks) { - return () -> getInvalidThreshold(); + return new SamplingIntent() { + @Override + public long getThreshold() { + return getInvalidThreshold(); + } + + @Override + public boolean isAdjustedCountReliable() { + return false; + } + }; } @Override diff --git a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentAnyOf.java b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentAnyOf.java index 56add2dfc..8cb7c028f 100644 --- a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentAnyOf.java +++ b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentAnyOf.java @@ -27,7 +27,7 @@ @Immutable final class ConsistentAnyOf extends ConsistentSampler { - private final ComposableSampler[] delegates; + private final Composable[] delegates; private final String description; @@ -36,7 +36,7 @@ final class ConsistentAnyOf extends ConsistentSampler { * * @param delegates the delegate samplers */ - ConsistentAnyOf(@Nullable ComposableSampler... delegates) { + ConsistentAnyOf(@Nullable Composable... delegates) { if (delegates == null || delegates.length == 0) { throw new IllegalArgumentException( "At least one delegate must be specified for ConsistentAnyOf"); @@ -59,23 +59,41 @@ public SamplingIntent getSamplingIntent( List parentLinks) { SamplingIntent[] intents = new SamplingIntent[delegates.length]; - int k = 0; + + // If any of the delegates provides a valid threshold, the resulting threshold is the minimum + // value T from the set of those valid threshold values, otherwise it is invalid threshold. long minimumThreshold = getInvalidThreshold(); - for (ComposableSampler delegate : delegates) { + + // If any of the delegates returning the threshold value equal to T returns true upon calling + // its IsAdjustedCountReliable() method, the resulting isAdjustedCountReliable is true, + // otherwise it is false. + boolean isAdjustedCountCorrect = false; + + int k = 0; + for (Composable delegate : delegates) { SamplingIntent delegateIntent = delegate.getSamplingIntent(parentContext, name, spanKind, attributes, parentLinks); long delegateThreshold = delegateIntent.getThreshold(); if (isValidThreshold(delegateThreshold)) { if (isValidThreshold(minimumThreshold)) { - minimumThreshold = Math.min(delegateThreshold, minimumThreshold); + if (delegateThreshold == minimumThreshold) { + if (delegateIntent.isAdjustedCountReliable()) { + isAdjustedCountCorrect = true; + } + } else if (delegateThreshold < minimumThreshold) { + minimumThreshold = delegateThreshold; + isAdjustedCountCorrect = delegateIntent.isAdjustedCountReliable(); + } } else { minimumThreshold = delegateThreshold; + isAdjustedCountCorrect = delegateIntent.isAdjustedCountReliable(); } } intents[k++] = delegateIntent; } long resultingThreshold = minimumThreshold; + boolean isResultingAdjustedCountCorrect = isAdjustedCountCorrect; return new SamplingIntent() { @Override @@ -83,6 +101,11 @@ public long getThreshold() { return resultingThreshold; } + @Override + public boolean isAdjustedCountReliable() { + return isResultingAdjustedCountCorrect; + } + @Override public Attributes getAttributes() { AttributesBuilder builder = Attributes.builder(); diff --git a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentParentBasedSampler.java b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentParentBasedSampler.java index 01e42ddcf..88583eacf 100644 --- a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentParentBasedSampler.java +++ b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentParentBasedSampler.java @@ -6,6 +6,7 @@ package io.opentelemetry.contrib.sampler.consistent56; import static io.opentelemetry.contrib.sampler.consistent56.ConsistentSamplingUtil.getInvalidThreshold; +import static io.opentelemetry.contrib.sampler.consistent56.ConsistentSamplingUtil.getMinThreshold; import static java.util.Objects.requireNonNull; import io.opentelemetry.api.common.Attributes; @@ -23,9 +24,9 @@ * sampling decision is delegated to the root sampler. */ @Immutable -final class ConsistentParentBasedSampler extends ConsistentSampler { +public class ConsistentParentBasedSampler extends ConsistentSampler { - private final ComposableSampler rootSampler; + private final Composable rootSampler; private final String description; @@ -35,14 +36,14 @@ final class ConsistentParentBasedSampler extends ConsistentSampler { * * @param rootSampler the root sampler */ - ConsistentParentBasedSampler(ComposableSampler rootSampler) { + protected ConsistentParentBasedSampler(Composable rootSampler) { this.rootSampler = requireNonNull(rootSampler); this.description = "ConsistentParentBasedSampler{rootSampler=" + rootSampler.getDescription() + '}'; } @Override - public SamplingIntent getSamplingIntent( + public final SamplingIntent getSamplingIntent( Context parentContext, String name, SpanKind spanKind, @@ -62,13 +63,51 @@ public SamplingIntent getSamplingIntent( OtelTraceState otelTraceState = OtelTraceState.parse(otelTraceStateString); long parentThreshold; + boolean isParentAdjustedCountCorrect; if (otelTraceState.hasValidThreshold()) { parentThreshold = otelTraceState.getThreshold(); + isParentAdjustedCountCorrect = true; } else { - parentThreshold = getInvalidThreshold(); + // If no threshold, look at the sampled flag + parentThreshold = parentSpanContext.isSampled() ? getMinThreshold() : getInvalidThreshold(); + isParentAdjustedCountCorrect = false; } - return () -> parentThreshold; + return new SamplingIntent() { + @Override + public long getThreshold() { + return parentThreshold; + } + + @Override + public boolean isAdjustedCountReliable() { + return isParentAdjustedCountCorrect; + } + + @Override + public Attributes getAttributes() { + if (parentSpanContext.isRemote()) { + return getAttributesWhenParentRemote(name, spanKind, attributes, parentLinks); + } else { + return getAttributesWhenParentLocal(name, spanKind, attributes, parentLinks); + } + } + + @Override + public TraceState updateTraceState(TraceState parentState) { + return parentState; + } + }; + } + + protected Attributes getAttributesWhenParentLocal( + String name, SpanKind spanKind, Attributes attributes, List parentLinks) { + return Attributes.empty(); + } + + protected Attributes getAttributesWhenParentRemote( + String name, SpanKind spanKind, Attributes attributes, List parentLinks) { + return Attributes.empty(); } @Override diff --git a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentRateLimitingSampler.java b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentRateLimitingSampler.java index d28161485..b58bee96a 100644 --- a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentRateLimitingSampler.java +++ b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentRateLimitingSampler.java @@ -122,7 +122,7 @@ public State( private final double targetSpansPerNanosecondLimit; private final double probabilitySmoothingFactor; private final AtomicReference state; - private final ComposableSampler delegate; + private final Composable delegate; /** * Constructor. @@ -133,7 +133,7 @@ public State( * @param nanoTimeSupplier a supplier for the current nano time */ ConsistentRateLimitingSampler( - ComposableSampler delegate, + Composable delegate, double targetSpansPerSecondLimit, double adaptationTimeSeconds, LongSupplier nanoTimeSupplier) { @@ -255,6 +255,11 @@ public long getThreshold() { return suggestedThreshold; } + @Override + public boolean isAdjustedCountReliable() { + return delegateIntent.isAdjustedCountReliable(); + } + @Override public Attributes getAttributes() { return delegateIntent.getAttributes(); diff --git a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentRuleBasedSampler.java b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentRuleBasedSampler.java index 90e4cb026..4d38a2212 100644 --- a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentRuleBasedSampler.java +++ b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentRuleBasedSampler.java @@ -19,8 +19,8 @@ /** * A consistent sampler that uses Span categorization and uses a different delegate sampler for each - * category. Categorization of Spans is aided by Predicates, which can be combined with - * ComposableSamplers into PredicatedSamplers. + * category. Categorization of Spans is aided by Predicates, which can be combined with Composables + * into PredicatedSamplers. */ @Immutable final class ConsistentRuleBasedSampler extends ConsistentSampler { diff --git a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentSampler.java b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentSampler.java index 5592b3215..1b2cedf08 100644 --- a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentSampler.java +++ b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentSampler.java @@ -24,7 +24,7 @@ /** Abstract base class for consistent samplers. */ @SuppressWarnings("InconsistentOverloads") -public abstract class ConsistentSampler implements Sampler, ComposableSampler { +public abstract class ConsistentSampler implements Sampler, Composable { /** * Returns a {@link ConsistentSampler} that samples all spans. @@ -61,7 +61,7 @@ public static ConsistentSampler probabilityBased(double samplingProbability) { * * @param rootSampler the root sampler */ - public static ConsistentSampler parentBased(ComposableSampler rootSampler) { + public static ConsistentSampler parentBased(Composable rootSampler) { return new ConsistentParentBasedSampler(rootSampler); } @@ -103,7 +103,7 @@ static ConsistentSampler rateLimited( * exponential smoothing) */ public static ConsistentSampler rateLimited( - ComposableSampler delegate, double targetSpansPerSecondLimit, double adaptationTimeSeconds) { + Composable delegate, double targetSpansPerSecondLimit, double adaptationTimeSeconds) { return rateLimited( delegate, targetSpansPerSecondLimit, adaptationTimeSeconds, System::nanoTime); } @@ -138,7 +138,7 @@ static ConsistentSampler rateLimited( * @param nanoTimeSupplier a supplier for the current nano time */ static ConsistentSampler rateLimited( - ComposableSampler delegate, + Composable delegate, double targetSpansPerSecondLimit, double adaptationTimeSeconds, LongSupplier nanoTimeSupplier) { @@ -159,7 +159,7 @@ static ConsistentSampler rateLimited( * @param delegates the delegate samplers, at least one delegate must be specified * @return the ConsistentAnyOf sampler */ - public static ConsistentSampler anyOf(ComposableSampler... delegates) { + public static ConsistentSampler anyOf(Composable... delegates) { return new ConsistentAnyOf(delegates); } @@ -184,19 +184,23 @@ public final SamplingResult shouldSample( // determine sampling decision boolean isSampled; + boolean isAdjustedCountCorrect; if (isValidThreshold(threshold)) { long randomness = getRandomness(otelTraceState, traceId); isSampled = threshold <= randomness; + isAdjustedCountCorrect = intent.isAdjustedCountReliable(); } else { // DROP isSampled = false; + isAdjustedCountCorrect = false; } - SamplingDecision samplingDecision; - if (isSampled) { - samplingDecision = SamplingDecision.RECORD_AND_SAMPLE; + SamplingDecision samplingDecision = + isSampled ? SamplingDecision.RECORD_AND_SAMPLE : SamplingDecision.DROP; + + // determine tracestate changes + if (isSampled && isAdjustedCountCorrect) { otelTraceState.setThreshold(threshold); } else { - samplingDecision = SamplingDecision.DROP; otelTraceState.invalidateThreshold(); } diff --git a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/Predicate.java b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/Predicate.java index 56ce59c46..7cbd706d6 100644 --- a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/Predicate.java +++ b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/Predicate.java @@ -38,6 +38,17 @@ static Predicate isRootSpan() { }; } + /* + * Return a Predicate that will only match Spans with local parent + */ + static Predicate hasLocalParent() { + return (parentContext, name, spanKind, attributes, parentLinks) -> { + Span parentSpan = Span.fromContext(parentContext); + SpanContext parentSpanContext = parentSpan.getSpanContext(); + return !parentSpanContext.isValid() || !parentSpanContext.isRemote(); + }; + } + /* * Return a Predicate that matches all Spans */ diff --git a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/PredicatedSampler.java b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/PredicatedSampler.java index dabec5b74..ac3bc7fa1 100644 --- a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/PredicatedSampler.java +++ b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/PredicatedSampler.java @@ -7,17 +7,17 @@ import static java.util.Objects.requireNonNull; -/** A class for holding a pair (Predicate, ComposableSampler) */ +/** A class for holding a pair (Predicate, Composable) */ public final class PredicatedSampler { - public static PredicatedSampler onMatch(Predicate predicate, ComposableSampler sampler) { + public static PredicatedSampler onMatch(Predicate predicate, Composable sampler) { return new PredicatedSampler(predicate, sampler); } private final Predicate predicate; - private final ComposableSampler sampler; + private final Composable sampler; - private PredicatedSampler(Predicate predicate, ComposableSampler sampler) { + private PredicatedSampler(Predicate predicate, Composable sampler) { this.predicate = requireNonNull(predicate); this.sampler = requireNonNull(sampler); } @@ -26,7 +26,7 @@ public Predicate getPredicate() { return predicate; } - public ComposableSampler getSampler() { + public Composable getSampler() { return sampler; } } diff --git a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/SamplingIntent.java b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/SamplingIntent.java index 07906ad3b..50a9b6716 100644 --- a/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/SamplingIntent.java +++ b/consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/SamplingIntent.java @@ -20,6 +20,13 @@ public interface SamplingIntent { */ long getThreshold(); + /* + * Return true if the adjusted count (calculated as reciprocal of the sampling probability) can be faithfully used to estimate span metrics. + */ + default boolean isAdjustedCountReliable() { + return true; + } + /** * Returns a set of Attributes to be added to the Span in case of positive sampling decision. * diff --git a/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/CoinFlipSampler.java b/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/CoinFlipSampler.java index a3999e954..c49b6b0e2 100644 --- a/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/CoinFlipSampler.java +++ b/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/CoinFlipSampler.java @@ -24,8 +24,8 @@ final class CoinFlipSampler extends ConsistentSampler { private static final SplittableRandom random = new SplittableRandom(0x160a50a2073e17e6L); - private final ComposableSampler samplerA; - private final ComposableSampler samplerB; + private final Composable samplerA; + private final Composable samplerB; private final double probability; private final String description; @@ -36,7 +36,7 @@ final class CoinFlipSampler extends ConsistentSampler { * @param samplerA the first delegate sampler * @param samplerB the second delegate sampler */ - CoinFlipSampler(ComposableSampler samplerA, ComposableSampler samplerB) { + CoinFlipSampler(Composable samplerA, Composable samplerB) { this(samplerA, samplerB, 0.5); } @@ -48,7 +48,7 @@ final class CoinFlipSampler extends ConsistentSampler { * @param samplerA the first delegate sampler * @param samplerB the second delegate sampler */ - CoinFlipSampler(ComposableSampler samplerA, ComposableSampler samplerB, double probability) { + CoinFlipSampler(Composable samplerA, Composable samplerB, double probability) { this.samplerA = requireNonNull(samplerA); this.samplerB = requireNonNull(samplerB); this.probability = probability; diff --git a/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentAnyOfTest.java b/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentAnyOfTest.java index 720a675e6..873ed04d3 100644 --- a/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentAnyOfTest.java +++ b/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentAnyOfTest.java @@ -9,26 +9,89 @@ import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.trace.data.LinkData; +import java.util.List; import org.junit.jupiter.api.Test; class ConsistentAnyOfTest { + static class TestSampler implements Composable { + private final long threshold; + private final boolean isAdjustedCountCorrect; + + public TestSampler(long threshold, boolean isAdjustedCountCorrect) { + this.threshold = threshold; + this.isAdjustedCountCorrect = isAdjustedCountCorrect; + } + + @Override + public SamplingIntent getSamplingIntent( + Context parentContext, + String name, + SpanKind spanKind, + Attributes attributes, + List parentLinks) { + + return new SamplingIntent() { + @Override + public long getThreshold() { + return threshold; + } + + @Override + public boolean isAdjustedCountReliable() { + return isAdjustedCountCorrect; + } + }; + } + + @Override + public String getDescription() { + return "MockSampler for tests"; + } + } + + @Test + void testMinimumThresholdWithAdjustedCount() { + Composable delegate1 = new TestSampler(0x80000000000000L, /* isAdjustedCountCorrect= */ false); + Composable delegate2 = new TestSampler(0x30000000000000L, /* isAdjustedCountCorrect= */ true); + Composable delegate3 = new TestSampler(0xa0000000000000L, /* isAdjustedCountCorrect= */ false); + Composable delegate4 = new TestSampler(0x30000000000000L, /* isAdjustedCountCorrect= */ false); + + Composable sampler = ConsistentSampler.anyOf(delegate1, delegate2, delegate3, delegate4); + SamplingIntent intent = sampler.getSamplingIntent(null, "span_name", null, null, null); + assertThat(intent.getThreshold()).isEqualTo(0x30000000000000L); + assertThat(intent.isAdjustedCountReliable()).isTrue(); + + // Change the delegate order + sampler = ConsistentSampler.anyOf(delegate1, delegate4, delegate3, delegate2); + intent = sampler.getSamplingIntent(null, "span_name", null, null, null); + assertThat(intent.getThreshold()).isEqualTo(0x30000000000000L); + assertThat(intent.isAdjustedCountReliable()).isTrue(); + } + @Test - void testMinimumThreshold() { - ComposableSampler delegate1 = new ConsistentFixedThresholdSampler(0x80000000000000L); - ComposableSampler delegate2 = new ConsistentFixedThresholdSampler(0x30000000000000L); - ComposableSampler delegate3 = new ConsistentFixedThresholdSampler(0xa0000000000000L); - ComposableSampler sampler = ConsistentSampler.anyOf(delegate1, delegate2, delegate3); + void testMinimumThresholdWithoutAdjustedCount() { + Composable delegate1 = new TestSampler(0x80000000000000L, /* isAdjustedCountCorrect= */ true); + Composable delegate2 = new TestSampler(0x30000000000000L, /* isAdjustedCountCorrect= */ false); + Composable delegate3 = new TestSampler(0xa0000000000000L, /* isAdjustedCountCorrect= */ true); + + Composable sampler = ConsistentSampler.anyOf(delegate1, delegate2, delegate3); SamplingIntent intent = sampler.getSamplingIntent(null, "span_name", null, null, null); assertThat(intent.getThreshold()).isEqualTo(0x30000000000000L); + assertThat(intent.isAdjustedCountReliable()).isFalse(); } @Test void testAlwaysDrop() { - ComposableSampler delegate1 = ConsistentSampler.alwaysOff(); - ComposableSampler sampler = ConsistentSampler.anyOf(delegate1); + Composable delegate1 = ConsistentSampler.alwaysOff(); + Composable sampler = ConsistentSampler.anyOf(delegate1); SamplingIntent intent = sampler.getSamplingIntent(null, "span_name", null, null, null); assertThat(intent.getThreshold()).isEqualTo(getInvalidThreshold()); + assertThat(intent.isAdjustedCountReliable()).isFalse(); } @Test @@ -36,27 +99,28 @@ void testSpanAttributesAdded() { AttributeKey key1 = AttributeKey.stringKey("tag1"); AttributeKey key2 = AttributeKey.stringKey("tag2"); AttributeKey key3 = AttributeKey.stringKey("tag3"); - ComposableSampler delegate1 = + Composable delegate1 = new MarkingSampler(new ConsistentFixedThresholdSampler(0x30000000000000L), key1, "a"); - ComposableSampler delegate2 = + Composable delegate2 = new MarkingSampler(new ConsistentFixedThresholdSampler(0x50000000000000L), key2, "b"); - ComposableSampler delegate3 = new MarkingSampler(ConsistentSampler.alwaysOff(), key3, "c"); - ComposableSampler sampler = ConsistentSampler.anyOf(delegate1, delegate2, delegate3); + Composable delegate3 = new MarkingSampler(ConsistentSampler.alwaysOff(), key3, "c"); + Composable sampler = ConsistentSampler.anyOf(delegate1, delegate2, delegate3); SamplingIntent intent = sampler.getSamplingIntent(null, "span_name", null, null, null); assertThat(intent.getAttributes().get(key1)).isEqualTo("a"); assertThat(intent.getAttributes().get(key2)).isEqualTo("b"); assertThat(intent.getAttributes().get(key3)).isEqualTo("c"); assertThat(intent.getThreshold()).isEqualTo(0x30000000000000L); + assertThat(intent.isAdjustedCountReliable()).isTrue(); } @Test void testSpanAttributeOverride() { AttributeKey key1 = AttributeKey.stringKey("shared"); - ComposableSampler delegate1 = + Composable delegate1 = new MarkingSampler(new ConsistentFixedThresholdSampler(0x30000000000000L), key1, "a"); - ComposableSampler delegate2 = + Composable delegate2 = new MarkingSampler(new ConsistentFixedThresholdSampler(0x50000000000000L), key1, "b"); - ComposableSampler sampler = ConsistentSampler.anyOf(delegate1, delegate2); + Composable sampler = ConsistentSampler.anyOf(delegate1, delegate2); SamplingIntent intent = sampler.getSamplingIntent(null, "span_name", null, null, null); assertThat(intent.getAttributes().get(key1)).isEqualTo("b"); } diff --git a/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentRateLimitingSamplerTest.java b/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentRateLimitingSamplerTest.java index 79bf064a0..cc56df1ef 100644 --- a/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentRateLimitingSamplerTest.java +++ b/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentRateLimitingSamplerTest.java @@ -64,7 +64,7 @@ void testConstantRate() { double targetSpansPerSecondLimit = 1000; double adaptationTimeSeconds = 5; - ComposableSampler delegate = + Composable delegate = new CoinFlipSampler(ConsistentSampler.alwaysOff(), ConsistentSampler.probabilityBased(0.8)); ConsistentSampler sampler = ConsistentSampler.rateLimited( @@ -105,7 +105,7 @@ void testConstantRateLowResolution() { double targetSpansPerSecondLimit = 1000; double adaptationTimeSeconds = 5; - ComposableSampler delegate = + Composable delegate = new CoinFlipSampler(ConsistentSampler.alwaysOff(), ConsistentSampler.probabilityBased(0.8)); ConsistentSampler sampler = ConsistentSampler.rateLimited( @@ -305,7 +305,7 @@ void testProportionalBehavior() { double adaptationTimeSeconds = 5; AttributeKey key = AttributeKey.stringKey("category"); - ComposableSampler delegate = + Composable delegate = new CoinFlipSampler( new MarkingSampler(ConsistentSampler.probabilityBased(0.6), key, "A"), new MarkingSampler(ConsistentSampler.probabilityBased(0.4), key, "B")); diff --git a/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentRuleBasedSamplerTest.java b/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentRuleBasedSamplerTest.java index bf79de0c5..44d015d71 100644 --- a/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentRuleBasedSamplerTest.java +++ b/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentRuleBasedSamplerTest.java @@ -16,7 +16,7 @@ class ConsistentRuleBasedSamplerTest { @Test void testEmptySet() { - ComposableSampler sampler = ConsistentSampler.ruleBased(SpanKind.SERVER); + Composable sampler = ConsistentSampler.ruleBased(SpanKind.SERVER); SamplingIntent intent = sampler.getSamplingIntent(null, "span_name", SpanKind.SERVER, null, null); assertThat(intent.getThreshold()).isEqualTo(getInvalidThreshold()); @@ -36,14 +36,14 @@ void testChoice() { AttributeKey key2 = AttributeKey.stringKey("tag2"); AttributeKey key3 = AttributeKey.stringKey("tag3"); - ComposableSampler delegate1 = + Composable delegate1 = new MarkingSampler(new ConsistentFixedThresholdSampler(0x80000000000000L), key1, "a"); - ComposableSampler delegate2 = + Composable delegate2 = new MarkingSampler(new ConsistentFixedThresholdSampler(0x50000000000000L), key2, "b"); - ComposableSampler delegate3 = + Composable delegate3 = new MarkingSampler(new ConsistentFixedThresholdSampler(0x30000000000000L), key3, "c"); - ComposableSampler sampler = + Composable sampler = ConsistentSampler.ruleBased( null, PredicatedSampler.onMatch(matchSpanName("A"), delegate1), @@ -79,7 +79,7 @@ void testChoice() { @Test void testSpanKindMatch() { - ComposableSampler sampler = + Composable sampler = ConsistentSampler.ruleBased( SpanKind.CLIENT, PredicatedSampler.onMatch(Predicate.anySpan(), ConsistentSampler.alwaysOn())); diff --git a/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentSamplerTest.java b/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentSamplerTest.java index e3f604020..a246e248f 100644 --- a/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentSamplerTest.java +++ b/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentSamplerTest.java @@ -199,6 +199,42 @@ void testMaxThreshold() { assertThat(output.getSampledFlag()).isFalse(); } + @Test + void testParentBasedInConsistentMode() { + + long parentRandomValue = 0x7f99aa40c02744L; + + Input input = new Input(); + input.setParentRandomValue(parentRandomValue); + input.setParentThreshold(parentRandomValue); + input.setParentSampled(false); // should be ignored + + ConsistentSampler sampler = ConsistentSampler.parentBased(ConsistentSampler.alwaysOn()); + + Output output = sample(input, sampler); + + assertThat(output.samplingResult.getDecision()).isEqualTo(SamplingDecision.RECORD_AND_SAMPLE); + assertThat(output.getThreshold()).hasValue(parentRandomValue); + assertThat(output.getRandomValue()).hasValue(parentRandomValue); + assertThat(output.getSampledFlag()).isTrue(); + } + + @Test + void testParentBasedInLegacyMode() { + + // No parent threshold present + Input input = new Input(); + + ConsistentSampler sampler = ConsistentSampler.parentBased(ConsistentSampler.alwaysOn()); + + Output output = sample(input, sampler); + + assertThat(output.samplingResult.getDecision()).isEqualTo(SamplingDecision.RECORD_AND_SAMPLE); + assertThat(output.getThreshold()).isNotPresent(); + assertThat(output.getRandomValue()).isNotPresent(); + assertThat(output.getSampledFlag()).isTrue(); + } + @Test void testHalfThresholdNotSampled() { diff --git a/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/MarkingSampler.java b/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/MarkingSampler.java index 687cd532a..3e691da9c 100644 --- a/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/MarkingSampler.java +++ b/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/MarkingSampler.java @@ -23,9 +23,9 @@ * could be also offered as a general utility. */ @Immutable -final class MarkingSampler implements ComposableSampler { +final class MarkingSampler implements Composable { - private final ComposableSampler delegate; + private final Composable delegate; private final AttributeKey attributeKey; private final String attributeValue; @@ -38,8 +38,7 @@ final class MarkingSampler implements ComposableSampler { * @param attributeKey Span attribute key * @param attributeValue Span attribute value */ - MarkingSampler( - ComposableSampler delegate, AttributeKey attributeKey, String attributeValue) { + MarkingSampler(Composable delegate, AttributeKey attributeKey, String attributeValue) { this.delegate = requireNonNull(delegate); this.attributeKey = requireNonNull(attributeKey); this.attributeValue = requireNonNull(attributeValue); diff --git a/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/UseCaseTest.java b/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/UseCaseTest.java index e164a66fb..5a4132478 100644 --- a/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/UseCaseTest.java +++ b/consistent-sampling/src/test/java/io/opentelemetry/contrib/sampler/consistent56/UseCaseTest.java @@ -67,7 +67,7 @@ private static ConsistentSampler buildSampler() { (parentContext, name, spanKind, attributes, parentLinks) -> { return "/checkout".equals(attributes.get(httpTarget)); }); - ComposableSampler s1 = + Composable s1 = ConsistentSampler.parentBased( ConsistentSampler.ruleBased( null, @@ -79,8 +79,8 @@ private static ConsistentSampler buildSampler() { return "/foo".equals(attributes.get(httpUrl)); }; - ComposableSampler s2 = ConsistentSampler.ruleBased(SpanKind.CLIENT, onMatch(foo, alwaysOn())); - ComposableSampler s3 = ConsistentSampler.anyOf(s1, s2); + Composable s2 = ConsistentSampler.ruleBased(SpanKind.CLIENT, onMatch(foo, alwaysOn())); + Composable s3 = ConsistentSampler.anyOf(s1, s2); return ConsistentSampler.rateLimited(s3, 1000.0, 5, UseCaseTest::nanoTime); }