Skip to content

Commit 0b9ab8a

Browse files
oertlanuraagajmacdtrask
authored
sampler and span processor prototype implementations for consistent sampling (#226)
* sampler and exporter implementations for consistent sampling * improved dependencies (in particular, removed dependency on guava) * Update consistent-sampling/src/main/java/io/opentelemetry/contrib/samplers/ConsistentParentBasedSampler.java Co-authored-by: Anuraag Agrawal <[email protected]> * reverted some changes * removed wrong immutable annotation * added javadoc * avoid else statements when returning * factory methods for consistent samplers, avoid exposure of implementations * added javadoc for AND and OR sampler composition * replaced use of synchronized by atomic reference * simplified thread local initialization * removed consistent reservoir sampling * improved comment * removed unnecessary clipping of sampling probability * added javadoc explaining maths of implementation * Update consistent-sampling/src/main/java/io/opentelemetry/contrib/state/OtelTraceState.java Co-authored-by: Joshua MacDonald <[email protected]> * Update consistent-sampling/src/main/java/io/opentelemetry/contrib/state/OtelTraceState.java Co-authored-by: Joshua MacDonald <[email protected]> * Update consistent-sampling/src/main/java/io/opentelemetry/contrib/state/OtelTraceState.java Co-authored-by: Joshua MacDonald <[email protected]> * Update consistent-sampling/src/main/java/io/opentelemetry/contrib/state/OtelTraceState.java Co-authored-by: Joshua MacDonald <[email protected]> * Update consistent-sampling/src/main/java/io/opentelemetry/contrib/samplers/ConsistentComposedAndSampler.java Co-authored-by: Trask Stalnaker <[email protected]> * Update consistent-sampling/src/main/java/io/opentelemetry/contrib/samplers/ConsistentComposedOrSampler.java Co-authored-by: Trask Stalnaker <[email protected]> * Update consistent-sampling/src/main/java/io/opentelemetry/contrib/samplers/ConsistentRateLimitingSampler.java Co-authored-by: Trask Stalnaker <[email protected]> * Update consistent-sampling/src/main/java/io/opentelemetry/contrib/samplers/ConsistentParentBasedSampler.java Co-authored-by: Trask Stalnaker <[email protected]> * Update consistent-sampling/src/main/java/io/opentelemetry/contrib/samplers/ConsistentSampler.java Co-authored-by: Trask Stalnaker <[email protected]> * added component owner for consistent sampling * Update consistent-sampling/src/main/java/io/opentelemetry/contrib/state/OtelTraceState.java Co-authored-by: Trask Stalnaker <[email protected]> * removed nonnull annotation * renamed variable s -> pair * renamed char parameter r -> c * renamed method isLowerCaseNum -> isDigit * use empty list instead of null for otherKeyValuePairs * simplified isValueByte method * Update consistent-sampling/src/main/java/io/opentelemetry/contrib/state/OtelTraceState.java Co-authored-by: Trask Stalnaker <[email protected]> * renamed variable sepPos -> separatorPos * replaced 0. and 1. by 0.0 and 1.0 * improved readability as suggested by @trask * removed unused methods from RandomUtil * added javadoc * renamed targetSpansPerNanosLimit -> targetSpansPerNanosecondLimit * throw IllegalArgumentException instead of returning NaN + added comments * renamed tsStartPos -> startPos and eqPos -> colonPos * improved readability of invariant check * added some more test cases * fixed typo * removed unused method * refactored random generator * made OtelTraceState and RandomGenerator package private and moved them to samplers package Co-authored-by: Anuraag Agrawal <[email protected]> Co-authored-by: Joshua MacDonald <[email protected]> Co-authored-by: Trask Stalnaker <[email protected]>
1 parent 7ca173b commit 0b9ab8a

17 files changed

+1782
-0
lines changed

.github/component_owners.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ components:
1313
aws-xray:
1414
- anuraaga
1515
- willarmiros
16+
consistent-sampling:
17+
- oertl
1618
samplers:
1719
- anuraaga
1820
- iNikem
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
plugins {
2+
id("otel.java-conventions")
3+
id("otel.publish-conventions")
4+
}
5+
6+
description = "Sampler and exporter implementations for consistent sampling"
7+
8+
dependencies {
9+
api("io.opentelemetry:opentelemetry-sdk-trace")
10+
testImplementation("org.hipparchus:hipparchus-core:2.0")
11+
testImplementation("org.hipparchus:hipparchus-stat:2.0")
12+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.samplers;
7+
8+
import javax.annotation.concurrent.Immutable;
9+
10+
@Immutable
11+
final class ConsistentAlwaysOffSampler extends ConsistentSampler {
12+
13+
private ConsistentAlwaysOffSampler() {}
14+
15+
private static final ConsistentSampler INSTANCE = new ConsistentAlwaysOffSampler();
16+
17+
static ConsistentSampler getInstance() {
18+
return INSTANCE;
19+
}
20+
21+
@Override
22+
protected int getP(int parentP, boolean isRoot) {
23+
return OtelTraceState.getMaxP();
24+
}
25+
26+
@Override
27+
public String getDescription() {
28+
return "ConsistentAlwaysOffSampler";
29+
}
30+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.samplers;
7+
8+
import javax.annotation.concurrent.Immutable;
9+
10+
@Immutable
11+
final class ConsistentAlwaysOnSampler extends ConsistentSampler {
12+
13+
private ConsistentAlwaysOnSampler() {}
14+
15+
private static final ConsistentSampler INSTANCE = new ConsistentAlwaysOnSampler();
16+
17+
static ConsistentSampler getInstance() {
18+
return INSTANCE;
19+
}
20+
21+
@Override
22+
protected int getP(int parentP, boolean isRoot) {
23+
return 0;
24+
}
25+
26+
@Override
27+
public String getDescription() {
28+
return "ConsistentAlwaysOnSampler";
29+
}
30+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.samplers;
7+
8+
import static java.util.Objects.requireNonNull;
9+
10+
import javax.annotation.concurrent.Immutable;
11+
12+
/**
13+
* A consistent sampler composed of two consistent samplers.
14+
*
15+
* <p>This sampler samples if both samplers would sample.
16+
*/
17+
@Immutable
18+
final class ConsistentComposedAndSampler extends ConsistentSampler {
19+
20+
private final ConsistentSampler sampler1;
21+
private final ConsistentSampler sampler2;
22+
private final String description;
23+
24+
ConsistentComposedAndSampler(ConsistentSampler sampler1, ConsistentSampler sampler2) {
25+
this.sampler1 = requireNonNull(sampler1);
26+
this.sampler2 = requireNonNull(sampler2);
27+
this.description =
28+
"ConsistentComposedAndSampler{"
29+
+ "sampler1="
30+
+ sampler1.getDescription()
31+
+ ",sampler2="
32+
+ sampler2.getDescription()
33+
+ '}';
34+
}
35+
36+
@Override
37+
protected int getP(int parentP, boolean isRoot) {
38+
int p1 = sampler1.getP(parentP, isRoot);
39+
int p2 = sampler2.getP(parentP, isRoot);
40+
if (OtelTraceState.isValidP(p1) && OtelTraceState.isValidP(p2)) {
41+
return Math.max(p1, p2);
42+
} else {
43+
return OtelTraceState.getInvalidP();
44+
}
45+
}
46+
47+
@Override
48+
public String getDescription() {
49+
return description;
50+
}
51+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.samplers;
7+
8+
import static java.util.Objects.requireNonNull;
9+
10+
import javax.annotation.concurrent.Immutable;
11+
12+
/**
13+
* A consistent sampler composed of two consistent samplers.
14+
*
15+
* <p>This sampler samples if any of the two samplers would sample.
16+
*/
17+
@Immutable
18+
final class ConsistentComposedOrSampler extends ConsistentSampler {
19+
20+
private final ConsistentSampler sampler1;
21+
private final ConsistentSampler sampler2;
22+
private final String description;
23+
24+
ConsistentComposedOrSampler(ConsistentSampler sampler1, ConsistentSampler sampler2) {
25+
this.sampler1 = requireNonNull(sampler1);
26+
this.sampler2 = requireNonNull(sampler2);
27+
this.description =
28+
"ConsistentComposedOrSampler{"
29+
+ "sampler1="
30+
+ sampler1.getDescription()
31+
+ ",sampler2="
32+
+ sampler2.getDescription()
33+
+ '}';
34+
}
35+
36+
@Override
37+
protected int getP(int parentP, boolean isRoot) {
38+
int p1 = sampler1.getP(parentP, isRoot);
39+
int p2 = sampler2.getP(parentP, isRoot);
40+
if (OtelTraceState.isValidP(p1)) {
41+
if (OtelTraceState.isValidP(p2)) {
42+
return Math.min(p1, p2);
43+
}
44+
return p1;
45+
} else {
46+
if (OtelTraceState.isValidP(p2)) {
47+
return p2;
48+
}
49+
return OtelTraceState.getInvalidP();
50+
}
51+
}
52+
53+
@Override
54+
public String getDescription() {
55+
return description;
56+
}
57+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.samplers;
7+
8+
import static java.util.Objects.requireNonNull;
9+
10+
import javax.annotation.concurrent.Immutable;
11+
12+
/**
13+
* A consistent sampler that makes the same sampling decision as the parent and optionally falls
14+
* back to an alternative consistent sampler, if the parent p-value is invalid (like for root
15+
* spans).
16+
*/
17+
@Immutable
18+
final class ConsistentParentBasedSampler extends ConsistentSampler {
19+
20+
private final ConsistentSampler rootSampler;
21+
22+
private final String description;
23+
24+
/**
25+
* Constructs a new consistent parent based sampler using the given root sampler.
26+
*
27+
* @param rootSampler the root sampler
28+
*/
29+
ConsistentParentBasedSampler(ConsistentSampler rootSampler) {
30+
this(rootSampler, RandomGenerator.getDefault());
31+
}
32+
33+
/**
34+
* Constructs a new consistent parent based sampler using the given root sampler and the given
35+
* thread-safe random generator.
36+
*
37+
* @param rootSampler the root sampler
38+
* @param threadSafeRandomGenerator a thread-safe random generator
39+
*/
40+
ConsistentParentBasedSampler(
41+
ConsistentSampler rootSampler, RandomGenerator threadSafeRandomGenerator) {
42+
super(threadSafeRandomGenerator);
43+
this.rootSampler = requireNonNull(rootSampler);
44+
this.description =
45+
"ConsistentParentBasedSampler{rootSampler=" + rootSampler.getDescription() + '}';
46+
}
47+
48+
@Override
49+
protected int getP(int parentP, boolean isRoot) {
50+
if (isRoot) {
51+
return rootSampler.getP(parentP, isRoot);
52+
} else {
53+
return parentP;
54+
}
55+
}
56+
57+
@Override
58+
public String getDescription() {
59+
return description;
60+
}
61+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.samplers;
7+
8+
import javax.annotation.concurrent.Immutable;
9+
10+
/** A consistent sampler that samples with a fixed probability. */
11+
@Immutable
12+
final class ConsistentProbabilityBasedSampler extends ConsistentSampler {
13+
14+
private final int lowerPValue;
15+
private final int upperPValue;
16+
private final double probabilityToUseLowerPValue;
17+
private final String description;
18+
19+
/**
20+
* Constructor.
21+
*
22+
* @param samplingProbability the sampling probability
23+
*/
24+
ConsistentProbabilityBasedSampler(double samplingProbability) {
25+
this(samplingProbability, RandomGenerator.getDefault());
26+
}
27+
28+
/**
29+
* Constructor.
30+
*
31+
* @param samplingProbability the sampling probability
32+
* @param randomGenerator a random generator
33+
*/
34+
ConsistentProbabilityBasedSampler(double samplingProbability, RandomGenerator randomGenerator) {
35+
super(randomGenerator);
36+
if (samplingProbability < 0.0 || samplingProbability > 1.0) {
37+
throw new IllegalArgumentException("Sampling probability must be in range [0.0, 1.0]!");
38+
}
39+
this.description =
40+
String.format("ConsistentProbabilityBasedSampler{%.6f}", samplingProbability);
41+
42+
lowerPValue = getLowerBoundP(samplingProbability);
43+
upperPValue = getUpperBoundP(samplingProbability);
44+
45+
if (lowerPValue == upperPValue) {
46+
probabilityToUseLowerPValue = 1;
47+
} else {
48+
double upperSamplingProbability = getSamplingProbability(lowerPValue);
49+
double lowerSamplingProbability = getSamplingProbability(upperPValue);
50+
probabilityToUseLowerPValue =
51+
(samplingProbability - lowerSamplingProbability)
52+
/ (upperSamplingProbability - lowerSamplingProbability);
53+
}
54+
}
55+
56+
@Override
57+
protected int getP(int parentP, boolean isRoot) {
58+
if (randomGenerator.nextBoolean(probabilityToUseLowerPValue)) {
59+
return lowerPValue;
60+
} else {
61+
return upperPValue;
62+
}
63+
}
64+
65+
@Override
66+
public String getDescription() {
67+
return description;
68+
}
69+
}

0 commit comments

Comments
 (0)