Skip to content

Commit ac36bb6

Browse files
authored
Improving inter-operation with legacy samplers (open-telemetry#1629)
1 parent 7a01c8c commit ac36bb6

17 files changed

+263
-65
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import java.util.List;
1313

1414
/** An interface for components to be used by composite consistent probability samplers. */
15-
public interface ComposableSampler {
15+
public interface Composable {
1616

1717
/**
1818
* Returns the SamplingIntent that is used for the sampling decision. The SamplingIntent includes

consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentAlwaysOffSampler.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,17 @@ public SamplingIntent getSamplingIntent(
3333
Attributes attributes,
3434
List<LinkData> parentLinks) {
3535

36-
return () -> getInvalidThreshold();
36+
return new SamplingIntent() {
37+
@Override
38+
public long getThreshold() {
39+
return getInvalidThreshold();
40+
}
41+
42+
@Override
43+
public boolean isAdjustedCountReliable() {
44+
return false;
45+
}
46+
};
3747
}
3848

3949
@Override

consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentAnyOf.java

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
@Immutable
2828
final class ConsistentAnyOf extends ConsistentSampler {
2929

30-
private final ComposableSampler[] delegates;
30+
private final Composable[] delegates;
3131

3232
private final String description;
3333

@@ -36,7 +36,7 @@ final class ConsistentAnyOf extends ConsistentSampler {
3636
*
3737
* @param delegates the delegate samplers
3838
*/
39-
ConsistentAnyOf(@Nullable ComposableSampler... delegates) {
39+
ConsistentAnyOf(@Nullable Composable... delegates) {
4040
if (delegates == null || delegates.length == 0) {
4141
throw new IllegalArgumentException(
4242
"At least one delegate must be specified for ConsistentAnyOf");
@@ -59,30 +59,53 @@ public SamplingIntent getSamplingIntent(
5959
List<LinkData> parentLinks) {
6060

6161
SamplingIntent[] intents = new SamplingIntent[delegates.length];
62-
int k = 0;
62+
63+
// If any of the delegates provides a valid threshold, the resulting threshold is the minimum
64+
// value T from the set of those valid threshold values, otherwise it is invalid threshold.
6365
long minimumThreshold = getInvalidThreshold();
64-
for (ComposableSampler delegate : delegates) {
66+
67+
// If any of the delegates returning the threshold value equal to T returns true upon calling
68+
// its IsAdjustedCountReliable() method, the resulting isAdjustedCountReliable is true,
69+
// otherwise it is false.
70+
boolean isAdjustedCountCorrect = false;
71+
72+
int k = 0;
73+
for (Composable delegate : delegates) {
6574
SamplingIntent delegateIntent =
6675
delegate.getSamplingIntent(parentContext, name, spanKind, attributes, parentLinks);
6776
long delegateThreshold = delegateIntent.getThreshold();
6877
if (isValidThreshold(delegateThreshold)) {
6978
if (isValidThreshold(minimumThreshold)) {
70-
minimumThreshold = Math.min(delegateThreshold, minimumThreshold);
79+
if (delegateThreshold == minimumThreshold) {
80+
if (delegateIntent.isAdjustedCountReliable()) {
81+
isAdjustedCountCorrect = true;
82+
}
83+
} else if (delegateThreshold < minimumThreshold) {
84+
minimumThreshold = delegateThreshold;
85+
isAdjustedCountCorrect = delegateIntent.isAdjustedCountReliable();
86+
}
7187
} else {
7288
minimumThreshold = delegateThreshold;
89+
isAdjustedCountCorrect = delegateIntent.isAdjustedCountReliable();
7390
}
7491
}
7592
intents[k++] = delegateIntent;
7693
}
7794

7895
long resultingThreshold = minimumThreshold;
96+
boolean isResultingAdjustedCountCorrect = isAdjustedCountCorrect;
7997

8098
return new SamplingIntent() {
8199
@Override
82100
public long getThreshold() {
83101
return resultingThreshold;
84102
}
85103

104+
@Override
105+
public boolean isAdjustedCountReliable() {
106+
return isResultingAdjustedCountCorrect;
107+
}
108+
86109
@Override
87110
public Attributes getAttributes() {
88111
AttributesBuilder builder = Attributes.builder();

consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentParentBasedSampler.java

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package io.opentelemetry.contrib.sampler.consistent56;
77

88
import static io.opentelemetry.contrib.sampler.consistent56.ConsistentSamplingUtil.getInvalidThreshold;
9+
import static io.opentelemetry.contrib.sampler.consistent56.ConsistentSamplingUtil.getMinThreshold;
910
import static java.util.Objects.requireNonNull;
1011

1112
import io.opentelemetry.api.common.Attributes;
@@ -23,9 +24,9 @@
2324
* sampling decision is delegated to the root sampler.
2425
*/
2526
@Immutable
26-
final class ConsistentParentBasedSampler extends ConsistentSampler {
27+
public class ConsistentParentBasedSampler extends ConsistentSampler {
2728

28-
private final ComposableSampler rootSampler;
29+
private final Composable rootSampler;
2930

3031
private final String description;
3132

@@ -35,14 +36,14 @@ final class ConsistentParentBasedSampler extends ConsistentSampler {
3536
*
3637
* @param rootSampler the root sampler
3738
*/
38-
ConsistentParentBasedSampler(ComposableSampler rootSampler) {
39+
protected ConsistentParentBasedSampler(Composable rootSampler) {
3940
this.rootSampler = requireNonNull(rootSampler);
4041
this.description =
4142
"ConsistentParentBasedSampler{rootSampler=" + rootSampler.getDescription() + '}';
4243
}
4344

4445
@Override
45-
public SamplingIntent getSamplingIntent(
46+
public final SamplingIntent getSamplingIntent(
4647
Context parentContext,
4748
String name,
4849
SpanKind spanKind,
@@ -62,13 +63,51 @@ public SamplingIntent getSamplingIntent(
6263
OtelTraceState otelTraceState = OtelTraceState.parse(otelTraceStateString);
6364

6465
long parentThreshold;
66+
boolean isParentAdjustedCountCorrect;
6567
if (otelTraceState.hasValidThreshold()) {
6668
parentThreshold = otelTraceState.getThreshold();
69+
isParentAdjustedCountCorrect = true;
6770
} else {
68-
parentThreshold = getInvalidThreshold();
71+
// If no threshold, look at the sampled flag
72+
parentThreshold = parentSpanContext.isSampled() ? getMinThreshold() : getInvalidThreshold();
73+
isParentAdjustedCountCorrect = false;
6974
}
7075

71-
return () -> parentThreshold;
76+
return new SamplingIntent() {
77+
@Override
78+
public long getThreshold() {
79+
return parentThreshold;
80+
}
81+
82+
@Override
83+
public boolean isAdjustedCountReliable() {
84+
return isParentAdjustedCountCorrect;
85+
}
86+
87+
@Override
88+
public Attributes getAttributes() {
89+
if (parentSpanContext.isRemote()) {
90+
return getAttributesWhenParentRemote(name, spanKind, attributes, parentLinks);
91+
} else {
92+
return getAttributesWhenParentLocal(name, spanKind, attributes, parentLinks);
93+
}
94+
}
95+
96+
@Override
97+
public TraceState updateTraceState(TraceState parentState) {
98+
return parentState;
99+
}
100+
};
101+
}
102+
103+
protected Attributes getAttributesWhenParentLocal(
104+
String name, SpanKind spanKind, Attributes attributes, List<LinkData> parentLinks) {
105+
return Attributes.empty();
106+
}
107+
108+
protected Attributes getAttributesWhenParentRemote(
109+
String name, SpanKind spanKind, Attributes attributes, List<LinkData> parentLinks) {
110+
return Attributes.empty();
72111
}
73112

74113
@Override

consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentRateLimitingSampler.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public State(
122122
private final double targetSpansPerNanosecondLimit;
123123
private final double probabilitySmoothingFactor;
124124
private final AtomicReference<State> state;
125-
private final ComposableSampler delegate;
125+
private final Composable delegate;
126126

127127
/**
128128
* Constructor.
@@ -133,7 +133,7 @@ public State(
133133
* @param nanoTimeSupplier a supplier for the current nano time
134134
*/
135135
ConsistentRateLimitingSampler(
136-
ComposableSampler delegate,
136+
Composable delegate,
137137
double targetSpansPerSecondLimit,
138138
double adaptationTimeSeconds,
139139
LongSupplier nanoTimeSupplier) {
@@ -255,6 +255,11 @@ public long getThreshold() {
255255
return suggestedThreshold;
256256
}
257257

258+
@Override
259+
public boolean isAdjustedCountReliable() {
260+
return delegateIntent.isAdjustedCountReliable();
261+
}
262+
258263
@Override
259264
public Attributes getAttributes() {
260265
return delegateIntent.getAttributes();

consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentRuleBasedSampler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919

2020
/**
2121
* A consistent sampler that uses Span categorization and uses a different delegate sampler for each
22-
* category. Categorization of Spans is aided by Predicates, which can be combined with
23-
* ComposableSamplers into PredicatedSamplers.
22+
* category. Categorization of Spans is aided by Predicates, which can be combined with Composables
23+
* into PredicatedSamplers.
2424
*/
2525
@Immutable
2626
final class ConsistentRuleBasedSampler extends ConsistentSampler {

consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/ConsistentSampler.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
/** Abstract base class for consistent samplers. */
2626
@SuppressWarnings("InconsistentOverloads")
27-
public abstract class ConsistentSampler implements Sampler, ComposableSampler {
27+
public abstract class ConsistentSampler implements Sampler, Composable {
2828

2929
/**
3030
* Returns a {@link ConsistentSampler} that samples all spans.
@@ -61,7 +61,7 @@ public static ConsistentSampler probabilityBased(double samplingProbability) {
6161
*
6262
* @param rootSampler the root sampler
6363
*/
64-
public static ConsistentSampler parentBased(ComposableSampler rootSampler) {
64+
public static ConsistentSampler parentBased(Composable rootSampler) {
6565
return new ConsistentParentBasedSampler(rootSampler);
6666
}
6767

@@ -103,7 +103,7 @@ static ConsistentSampler rateLimited(
103103
* exponential smoothing)
104104
*/
105105
public static ConsistentSampler rateLimited(
106-
ComposableSampler delegate, double targetSpansPerSecondLimit, double adaptationTimeSeconds) {
106+
Composable delegate, double targetSpansPerSecondLimit, double adaptationTimeSeconds) {
107107
return rateLimited(
108108
delegate, targetSpansPerSecondLimit, adaptationTimeSeconds, System::nanoTime);
109109
}
@@ -138,7 +138,7 @@ static ConsistentSampler rateLimited(
138138
* @param nanoTimeSupplier a supplier for the current nano time
139139
*/
140140
static ConsistentSampler rateLimited(
141-
ComposableSampler delegate,
141+
Composable delegate,
142142
double targetSpansPerSecondLimit,
143143
double adaptationTimeSeconds,
144144
LongSupplier nanoTimeSupplier) {
@@ -159,7 +159,7 @@ static ConsistentSampler rateLimited(
159159
* @param delegates the delegate samplers, at least one delegate must be specified
160160
* @return the ConsistentAnyOf sampler
161161
*/
162-
public static ConsistentSampler anyOf(ComposableSampler... delegates) {
162+
public static ConsistentSampler anyOf(Composable... delegates) {
163163
return new ConsistentAnyOf(delegates);
164164
}
165165

@@ -184,19 +184,23 @@ public final SamplingResult shouldSample(
184184

185185
// determine sampling decision
186186
boolean isSampled;
187+
boolean isAdjustedCountCorrect;
187188
if (isValidThreshold(threshold)) {
188189
long randomness = getRandomness(otelTraceState, traceId);
189190
isSampled = threshold <= randomness;
191+
isAdjustedCountCorrect = intent.isAdjustedCountReliable();
190192
} else { // DROP
191193
isSampled = false;
194+
isAdjustedCountCorrect = false;
192195
}
193196

194-
SamplingDecision samplingDecision;
195-
if (isSampled) {
196-
samplingDecision = SamplingDecision.RECORD_AND_SAMPLE;
197+
SamplingDecision samplingDecision =
198+
isSampled ? SamplingDecision.RECORD_AND_SAMPLE : SamplingDecision.DROP;
199+
200+
// determine tracestate changes
201+
if (isSampled && isAdjustedCountCorrect) {
197202
otelTraceState.setThreshold(threshold);
198203
} else {
199-
samplingDecision = SamplingDecision.DROP;
200204
otelTraceState.invalidateThreshold();
201205
}
202206

consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/Predicate.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ static Predicate isRootSpan() {
3838
};
3939
}
4040

41+
/*
42+
* Return a Predicate that will only match Spans with local parent
43+
*/
44+
static Predicate hasLocalParent() {
45+
return (parentContext, name, spanKind, attributes, parentLinks) -> {
46+
Span parentSpan = Span.fromContext(parentContext);
47+
SpanContext parentSpanContext = parentSpan.getSpanContext();
48+
return !parentSpanContext.isValid() || !parentSpanContext.isRemote();
49+
};
50+
}
51+
4152
/*
4253
* Return a Predicate that matches all Spans
4354
*/

consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/PredicatedSampler.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@
77

88
import static java.util.Objects.requireNonNull;
99

10-
/** A class for holding a pair (Predicate, ComposableSampler) */
10+
/** A class for holding a pair (Predicate, Composable) */
1111
public final class PredicatedSampler {
1212

13-
public static PredicatedSampler onMatch(Predicate predicate, ComposableSampler sampler) {
13+
public static PredicatedSampler onMatch(Predicate predicate, Composable sampler) {
1414
return new PredicatedSampler(predicate, sampler);
1515
}
1616

1717
private final Predicate predicate;
18-
private final ComposableSampler sampler;
18+
private final Composable sampler;
1919

20-
private PredicatedSampler(Predicate predicate, ComposableSampler sampler) {
20+
private PredicatedSampler(Predicate predicate, Composable sampler) {
2121
this.predicate = requireNonNull(predicate);
2222
this.sampler = requireNonNull(sampler);
2323
}
@@ -26,7 +26,7 @@ public Predicate getPredicate() {
2626
return predicate;
2727
}
2828

29-
public ComposableSampler getSampler() {
29+
public Composable getSampler() {
3030
return sampler;
3131
}
3232
}

consistent-sampling/src/main/java/io/opentelemetry/contrib/sampler/consistent56/SamplingIntent.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ public interface SamplingIntent {
2020
*/
2121
long getThreshold();
2222

23+
/*
24+
* Return true if the adjusted count (calculated as reciprocal of the sampling probability) can be faithfully used to estimate span metrics.
25+
*/
26+
default boolean isAdjustedCountReliable() {
27+
return true;
28+
}
29+
2330
/**
2431
* Returns a set of Attributes to be added to the Span in case of positive sampling decision.
2532
*

0 commit comments

Comments
 (0)