Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sampler and span processor prototype implementations for consistent sampling #226

Merged
merged 54 commits into from
Apr 20, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
7116c5e
sampler and exporter implementations for consistent sampling
oertl Feb 4, 2022
d8dd6e6
Merge branch 'main' into consistent-sampling
oertl Feb 7, 2022
6b4ba10
improved dependencies (in particular, removed dependency on guava)
oertl Feb 14, 2022
5ca2625
Merge remote-tracking branch 'origin' into consistent-sampling
oertl Feb 14, 2022
038f803
Update consistent-sampling/src/main/java/io/opentelemetry/contrib/sam…
oertl Feb 14, 2022
a686b3a
reverted some changes
oertl Feb 14, 2022
08bbc40
removed wrong immutable annotation
oertl Feb 15, 2022
9258ef4
added javadoc
oertl Feb 15, 2022
000ffea
avoid else statements when returning
oertl Feb 15, 2022
f7911c3
factory methods for consistent samplers, avoid exposure of implementa…
oertl Feb 15, 2022
b3459f9
added javadoc for AND and OR sampler composition
oertl Feb 15, 2022
0bbdb73
Merge remote-tracking branch 'origin' into consistent-sampling
oertl Feb 15, 2022
185f579
replaced use of synchronized by atomic reference
oertl Feb 15, 2022
abb7e71
simplified thread local initialization
oertl Feb 15, 2022
dd3df4e
removed consistent reservoir sampling
oertl Feb 16, 2022
90a2960
Merge branch 'open-telemetry:main' into consistent-sampling
oertl Mar 9, 2022
6f731b6
improved comment
oertl Mar 9, 2022
d40c0f1
removed unnecessary clipping of sampling probability
oertl Mar 10, 2022
775ca52
added javadoc explaining maths of implementation
oertl Mar 10, 2022
2ffe08f
Merge branch 'open-telemetry:main' into consistent-sampling
oertl Mar 10, 2022
a82eee6
Update consistent-sampling/src/main/java/io/opentelemetry/contrib/sta…
oertl Apr 9, 2022
6e8a38a
Update consistent-sampling/src/main/java/io/opentelemetry/contrib/sta…
oertl Apr 9, 2022
ae9670b
Update consistent-sampling/src/main/java/io/opentelemetry/contrib/sta…
oertl Apr 9, 2022
83c21f4
Update consistent-sampling/src/main/java/io/opentelemetry/contrib/sta…
oertl Apr 9, 2022
35d0373
Update consistent-sampling/src/main/java/io/opentelemetry/contrib/sam…
oertl Apr 19, 2022
348a240
Update consistent-sampling/src/main/java/io/opentelemetry/contrib/sam…
oertl Apr 19, 2022
0afaa2f
Update consistent-sampling/src/main/java/io/opentelemetry/contrib/sam…
oertl Apr 19, 2022
f63bb0b
Update consistent-sampling/src/main/java/io/opentelemetry/contrib/sam…
oertl Apr 19, 2022
aaffdd7
Update consistent-sampling/src/main/java/io/opentelemetry/contrib/sam…
oertl Apr 19, 2022
56eec39
added component owner for consistent sampling
oertl Apr 19, 2022
667e1f7
Update consistent-sampling/src/main/java/io/opentelemetry/contrib/sta…
oertl Apr 19, 2022
7910939
Merge branch 'consistent-sampling' of github.com:dynatrace-oss-contri…
oertl Apr 19, 2022
3fae630
removed nonnull annotation
oertl Apr 19, 2022
5040a74
renamed variable s -> pair
oertl Apr 19, 2022
e0041c0
renamed char parameter r -> c
oertl Apr 19, 2022
767e52f
renamed method isLowerCaseNum -> isDigit
oertl Apr 19, 2022
c18dcfe
use empty list instead of null for otherKeyValuePairs
oertl Apr 19, 2022
be68ff2
simplified isValueByte method
oertl Apr 19, 2022
30e42e3
Update consistent-sampling/src/main/java/io/opentelemetry/contrib/sta…
oertl Apr 19, 2022
d209258
Merge branch 'consistent-sampling' of github.com:dynatrace-oss-contri…
oertl Apr 19, 2022
7f58aa1
renamed variable sepPos -> separatorPos
oertl Apr 19, 2022
938382a
replaced 0. and 1. by 0.0 and 1.0
oertl Apr 19, 2022
630b157
improved readability as suggested by @trask
oertl Apr 19, 2022
1dca6f4
removed unused methods from RandomUtil
oertl Apr 19, 2022
09cc3a0
added javadoc
oertl Apr 19, 2022
15a8d50
renamed targetSpansPerNanosLimit -> targetSpansPerNanosecondLimit
oertl Apr 19, 2022
f70454a
throw IllegalArgumentException instead of returning NaN + added comments
oertl Apr 19, 2022
8d3a731
renamed tsStartPos -> startPos and eqPos -> colonPos
oertl Apr 19, 2022
3b7f045
improved readability of invariant check
oertl Apr 19, 2022
878c236
added some more test cases
oertl Apr 19, 2022
6fc45cd
fixed typo
oertl Apr 19, 2022
06cbeea
removed unused method
oertl Apr 19, 2022
edac8a9
refactored random generator
oertl Apr 19, 2022
da42018
made OtelTraceState and RandomGenerator package private and moved the…
oertl Apr 20, 2022
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
13 changes: 13 additions & 0 deletions consistent-sampling/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
plugins {
id("otel.java-conventions")
id("otel.publish-conventions")
}

description = "Sampler and exporter implementations for consistent sampling"

dependencies {
api("io.opentelemetry:opentelemetry-sdk")
oertl marked this conversation as resolved.
Show resolved Hide resolved
testImplementation("com.google.guava:guava:31.0.1-jre")
oertl marked this conversation as resolved.
Show resolved Hide resolved
testImplementation("org.hipparchus:hipparchus-core:2.0")
testImplementation("org.hipparchus:hipparchus-stat:2.0")
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.export;

import static io.opentelemetry.api.internal.Utils.checkArgument;
import static java.util.Objects.requireNonNull;

import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.contrib.util.RandomGenerator;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.time.Duration;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

/** Builder class for {@link ConsistentReservoirSamplingBatchSpanProcessorBuilder}. */
public final class ConsistentReservoirSamplingBatchSpanProcessorBuilder {

// Visible for testing
static final long DEFAULT_SCHEDULE_DELAY_MILLIS = 5000;
// Visible for testing
static final int DEFAULT_RESERVOIR_SIZE = 2048;
// Visible for testing
static final int DEFAULT_EXPORT_TIMEOUT_MILLIS = 30_000;

private final SpanExporter spanExporter;
private long scheduleDelayNanos = TimeUnit.MILLISECONDS.toNanos(DEFAULT_SCHEDULE_DELAY_MILLIS);
private int reservoirSize = DEFAULT_RESERVOIR_SIZE;
private long exporterTimeoutNanos = TimeUnit.MILLISECONDS.toNanos(DEFAULT_EXPORT_TIMEOUT_MILLIS);
private MeterProvider meterProvider = MeterProvider.noop();
private RandomGenerator threadSafeRandomGenerator = () -> ThreadLocalRandom.current().nextLong();
private boolean useAlternativeReservoirImplementation = false;

ConsistentReservoirSamplingBatchSpanProcessorBuilder(SpanExporter spanExporter) {
this.spanExporter = requireNonNull(spanExporter, "spanExporter");
}

// TODO: Consider to add support for constant Attributes and/or Resource.

/**
* Sets the delay interval between two consecutive exports. If unset, defaults to {@value
* DEFAULT_SCHEDULE_DELAY_MILLIS}ms.
*/
public ConsistentReservoirSamplingBatchSpanProcessorBuilder setScheduleDelay(
long delay, TimeUnit unit) {
requireNonNull(unit, "unit");
checkArgument(delay >= 0, "delay must be non-negative");
scheduleDelayNanos = unit.toNanos(delay);
return this;
}

/**
* Sets the delay interval between two consecutive exports. If unset, defaults to {@value
* DEFAULT_SCHEDULE_DELAY_MILLIS}ms.
*/
public ConsistentReservoirSamplingBatchSpanProcessorBuilder setScheduleDelay(Duration delay) {
requireNonNull(delay, "delay");
return setScheduleDelay(delay.toNanos(), TimeUnit.NANOSECONDS);
}

// Visible for testing
long getScheduleDelayNanos() {
return scheduleDelayNanos;
}

/**
* Sets the maximum time an export will be allowed to run before being cancelled. If unset,
* defaults to {@value DEFAULT_EXPORT_TIMEOUT_MILLIS}ms.
*/
public ConsistentReservoirSamplingBatchSpanProcessorBuilder setExporterTimeout(
long timeout, TimeUnit unit) {
requireNonNull(unit, "unit");
checkArgument(timeout >= 0, "timeout must be non-negative");
exporterTimeoutNanos = unit.toNanos(timeout);
return this;
}

/**
* Sets the maximum time an export will be allowed to run before being cancelled. If unset,
* defaults to {@value DEFAULT_EXPORT_TIMEOUT_MILLIS}ms.
*/
public ConsistentReservoirSamplingBatchSpanProcessorBuilder setExporterTimeout(Duration timeout) {
requireNonNull(timeout, "timeout");
return setExporterTimeout(timeout.toNanos(), TimeUnit.NANOSECONDS);
}

// Visible for testing
long getExporterTimeoutNanos() {
return exporterTimeoutNanos;
}

/**
* Sets the reservoir size, themaximum number of Spans that can be collected.
*
* <p>See the ConsistentReservoirSamplingBatchSpanProcessor class description for a high-level
* design description of this class.
*
* <p>Default value is {@code 2048}.
*
* @param reservoirSize the reservoir size, the maximum number of Spans that are kept
* @return this.
* @see ConsistentReservoirSamplingBatchSpanProcessorBuilder#DEFAULT_RESERVOIR_SIZE
*/
public ConsistentReservoirSamplingBatchSpanProcessorBuilder setReservoirSize(int reservoirSize) {
this.reservoirSize = reservoirSize;
return this;
}

// Visible for testing
int getReservoirSize() {
return reservoirSize;
}

/**
* Sets the {@link MeterProvider} to use to collect metrics related to batch export. If not set,
* metrics will not be collected.
*/
public ConsistentReservoirSamplingBatchSpanProcessorBuilder setMeterProvider(
MeterProvider meterProvider) {
requireNonNull(meterProvider, "meterProvider");
this.meterProvider = meterProvider;
return this;
}

// Visible for testing
ConsistentReservoirSamplingBatchSpanProcessorBuilder setThreadSafeRandomGenerator(
RandomGenerator threadSafeRandomGenerator) {
this.threadSafeRandomGenerator = threadSafeRandomGenerator;
return this;
}

// Visible for testing
ConsistentReservoirSamplingBatchSpanProcessorBuilder useAlternativeReservoirImplementation(
boolean useAlternativeReservoirImplementation) {
this.useAlternativeReservoirImplementation = useAlternativeReservoirImplementation;
return this;
}

/**
* Returns a new {@link ConsistentReservoirSamplingBatchSpanProcessorBuilder} that batches, then
* converts spans to proto and forwards them to the given {@code spanExporter}.
*
* @return a new {@link ConsistentReservoirSamplingBatchSpanProcessorBuilder}.
* @throws NullPointerException if the {@code spanExporter} is {@code null}.
*/
public ConsistentReservoirSamplingBatchSpanProcessor build() {
return new ConsistentReservoirSamplingBatchSpanProcessor(
spanExporter,
meterProvider,
scheduleDelayNanos,
reservoirSize,
exporterTimeoutNanos,
threadSafeRandomGenerator,
useAlternativeReservoirImplementation);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.samplers;

import io.opentelemetry.contrib.state.OtelTraceState;
import javax.annotation.concurrent.Immutable;

@Immutable
public final class ConsistentAlwaysOffSampler extends ConsistentSampler {
oertl marked this conversation as resolved.
Show resolved Hide resolved

@Override
protected int getP(int parentP, boolean isRoot) {
return OtelTraceState.getMaxP();
}

@Override
public String getDescription() {
return "ConsistentAlwaysOffSampler";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.samplers;

import javax.annotation.concurrent.Immutable;

@Immutable
public class ConsistentAlwaysOnSampler extends ConsistentSampler {

@Override
protected int getP(int parentP, boolean isRoot) {
return 0;
}

@Override
public String getDescription() {
return "ConsistentAlwaysOnSampler";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.samplers;

import static java.util.Objects.requireNonNull;

import io.opentelemetry.contrib.state.OtelTraceState;
import javax.annotation.concurrent.Immutable;

/**
* A consistent sampler composes two consistent samplers.
oertl marked this conversation as resolved.
Show resolved Hide resolved
*
* <p>This sampler samples if both samplers would sample.
oertl marked this conversation as resolved.
Show resolved Hide resolved
*/
@Immutable
public final class ConsistentComposedAndSampler extends ConsistentSampler {

private final ConsistentSampler sampler1;
private final ConsistentSampler sampler2;
private final String description;

public static ConsistentComposedAndSampler create(
ConsistentSampler sampler1, ConsistentSampler sampler2) {
return new ConsistentComposedAndSampler(sampler1, sampler2);
}

private ConsistentComposedAndSampler(ConsistentSampler sampler1, ConsistentSampler sampler2) {
this.sampler1 = requireNonNull(sampler1);
this.sampler2 = requireNonNull(sampler2);
this.description =
"ConsistentComposedAndSampler{"
+ "sampler1="
+ sampler1.getDescription()
+ ",sampler2="
+ sampler2.getDescription()
+ '}';
}

@Override
protected int getP(int parentP, boolean isRoot) {
int p1 = sampler1.getP(parentP, isRoot);
int p2 = sampler2.getP(parentP, isRoot);
if (OtelTraceState.isValidP(p1) && OtelTraceState.isValidP(p2)) {
return Math.max(p1, p2);
} else {
return OtelTraceState.getInvalidP();
}
}

@Override
public String getDescription() {
return description;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.samplers;

import static java.util.Objects.requireNonNull;

import io.opentelemetry.contrib.state.OtelTraceState;
import javax.annotation.concurrent.Immutable;

/**
* A consistent sampler composes two consistent samplers.
oertl marked this conversation as resolved.
Show resolved Hide resolved
*
* <p>This sampler samples if any of the two samplers would sample.
*/
@Immutable
public final class ConsistentComposedOrSampler extends ConsistentSampler {

private final ConsistentSampler sampler1;
private final ConsistentSampler sampler2;
private final String description;

public static ConsistentComposedOrSampler create(
ConsistentSampler sampler1, ConsistentSampler sampler2) {
return new ConsistentComposedOrSampler(sampler1, sampler2);
}

private ConsistentComposedOrSampler(ConsistentSampler sampler1, ConsistentSampler sampler2) {
this.sampler1 = requireNonNull(sampler1);
this.sampler2 = requireNonNull(sampler2);
this.description =
"ConsistentComposedOrSampler{"
+ "sampler1="
+ sampler1.getDescription()
+ ",sampler2="
+ sampler2.getDescription()
+ '}';
}

@Override
protected int getP(int parentP, boolean isRoot) {
int p1 = sampler1.getP(parentP, isRoot);
int p2 = sampler2.getP(parentP, isRoot);
if (OtelTraceState.isValidP(p1)) {
if (OtelTraceState.isValidP(p2)) {
return Math.min(p1, p2);
} else {
oertl marked this conversation as resolved.
Show resolved Hide resolved
return p1;
}
} else {
if (OtelTraceState.isValidP(p2)) {
return p2;
} else {
return OtelTraceState.getInvalidP();
}
}
}

@Override
public String getDescription() {
return description;
}
}
Loading