Skip to content
Open
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
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,39 @@

## Unreleased

### Consistent sampling

- **Breaking change**: the consistent sampling module has been rebuilt on top
of the OpenTelemetry SDK incubator composable-samplers API in
`opentelemetry-sdk-extension-incubator`. Both the legacy OTEP 4673 implementation in
`io.opentelemetry.contrib.sampler.consistent` and the newer `consistent56`
package have been collapsed into a single `io.opentelemetry.contrib.sampler.consistent`
package that uses the SDK incubator implementations where available.
Removed classes (use the SDK incubator equivalents instead):
`ConsistentProbabilityBasedSampler`, `ConsistentComposedAndSampler`,
`ConsistentComposedOrSampler`, `ConsistentAlwaysOnSampler`,
`ConsistentAlwaysOffSampler`, `ConsistentParentBasedSampler`,
`ConsistentFixedThresholdSampler`, `ConsistentRuleBasedSampler`,
`Composable`, `SamplingIntent`, `RandomValueGenerator`,
`RandomValueGenerators`, `Predicate`, `PredicatedSampler`,
`RValueGenerator`, `RValueGenerators`. The remaining contrib-only samplers
(`ConsistentRateLimitingSampler`, `ConsistentVariableThresholdSampler`,
`ConsistentAnyOf`) now implement the SDK incubator
`io.opentelemetry.sdk.extension.incubator.trace.samplers.ComposableSampler`
interface, and `ConsistentReservoirSamplingSpanProcessor` is unchanged.
Use `CompositeSampler.wrap(composable)` to turn a composable sampler into
a `Sampler`.
- The `parentbased_consistent_probability` autoconfigure sampler name is
retained; the provider now builds its sampler on top of the SDK incubator
`ComposableSampler` / `CompositeSampler` API and produces a sampler
equivalent to
`CompositeSampler.wrap(ComposableSampler.parentThreshold(ComposableSampler.probability(p)))`.
- Migration mapping for the removed `ConsistentSampler` factories:
- `ConsistentSampler.alwaysOn()` -> `ComposableSampler.alwaysOn()`
- `ConsistentSampler.alwaysOff()` -> `ComposableSampler.alwaysOff()`
- `ConsistentSampler.probabilityBased(p)` -> `ComposableSampler.probability(p)`
- `ConsistentSampler.parentBased(root)` -> `ComposableSampler.parentThreshold(root)`

### Dynamic control

- Add SourceFormat string to enum conversion
Expand Down
60 changes: 14 additions & 46 deletions consistent-sampling/README.md
Original file line number Diff line number Diff line change
@@ -1,59 +1,27 @@
# Consistent sampling

There are two major components included here.

## Original proposal implementation

The original specification for consistent probability sampling is defined by
<https://github.com/open-telemetry/opentelemetry-specification/blob/main/oteps/trace/4673-experimental-probability-sampling.md>
and <https://github.com/open-telemetry/opentelemetry-specification/pull/2047>.
It supports sampling probabilities that are power of 2 (1, 1/2, 1/4, ...), and uses 8-bit `r-value` and 8-bit `p-value` in tracestate.

The implementation of this proposal is contained by the package `io/opentelemetry/contrib/sampler/consistent` in this repository and provides various Sampler implementations.

* **ConsistentSampler**:
abstract base class of all consistent sampler implementations below
* **ConsistentAlwaysOffSampler**:
see <https://github.com/open-telemetry/opentelemetry-specification/blob/main/oteps/trace/4673-experimental-probability-sampling.md#always-off-sampler>
* **ConsistentAlwaysOnSampler**:
see <https://github.com/open-telemetry/opentelemetry-specification/blob/main/oteps/trace/4673-experimental-probability-sampling.md#always-on-consistent-probability-sampler>
* **ConsistentComposedAndSampler**:
allows combining two consistent samplers and samples when both samplers would sample
* **ConsistentComposedOrSampler**:
allows combining two consistent sampler and samples when at least one of both samplers would sample,
see <https://github.com/open-telemetry/opentelemetry-specification/blob/main/oteps/trace/4673-experimental-probability-sampling.md#requirement-combine-multiple-consistent-probability-samplers-using-the-minimum-p-value>
* **ConsistentParentBasedSampler**:
see <https://github.com/open-telemetry/opentelemetry-specification/blob/main/oteps/trace/4673-experimental-probability-sampling.md#parentconsistentprobabilitybased-sampler>
* **ConsistentProbabilityBasedSampler**:
see <https://github.com/open-telemetry/opentelemetry-specification/blob/main/oteps/trace/4673-experimental-probability-sampling.md#consistentprobabilitybased-sampler>
* **ConsistentRateLimitingSampler**:
a rate limiting sampler based on exponential smoothing that dynamically adjusts the sampling
probability based on the estimated rate of spans occurring to satisfy a given rate of sampled spans

## Current proposal implementation

The current version of the specification for consistent probability sampling is described by
<https://github.com/open-telemetry/oteps/blob/main/text/trace/0235-sampling-threshold-in-trace-state.md>.
It uses **56** bits for representing _rejection threshold_, which corresponds to a much wider range of sampling probabilities than the original proposal.

The implementation of the current proposal is contained by the package `io/opentelemetry/contrib/sampler/consistent56` in this repository and provides implementation for a number of different Samplers.
The implementation is contained by the package `io/opentelemetry/contrib/sampler/consistent` in this repository.
The OpenTelemetry SDK incubator module provides the composable-samplers building blocks
(`ComposableSampler`, `CompositeSampler`, and the `alwaysOn`/`alwaysOff`/`probability`/
`parentThreshold`/`ruleBasedBuilder`/`annotating` factories) in
`opentelemetry-sdk-extension-incubator` under
`io.opentelemetry.sdk.extension.incubator.trace.samplers`. This module adds the following
contrib-only APIs:

* **ConsistentSampler**
abstract base class for all consistent sampler implementations
* **ComposableSampler**:
interface used to build hierarchies of Samplers, see [Composite Samplers](https://github.com/open-telemetry/oteps/pull/250)
* **ConsistentAlwaysOffSampler**:
* **ConsistentAlwaysOnSampler**:
* **ConsistentAnyOfSampler**:
allows combining several consistent samplers; it samples when at least one of them would sample,
* **ConsistentParentBasedSampler**:
* **ConsistentFixedThresholdSampler**:
consistent probability sampler that uses a predefined sampling probability
* **ConsistentSampler**:
convenience factory for the contrib-only samplers below; for the common samplers use
`ComposableSampler`'s static factories directly
* **ConsistentAnyOf**:
allows combining several composable samplers; it samples when at least one of them would sample
* **ConsistentRateLimitingSampler**:
a rate limiting sampler based on exponential smoothing that dynamically adjusts the sampling
probability based on the estimated rate of spans occurring to satisfy a given rate of sampled spans
* **ConsistentRuleBasedSampler**
a sampler that performs stratified sampling by evaluating qualifying conditions and propagating the sampling decision from one of its delegate samplers
* **ConsistentVariableThresholdSampler**:
consistent probability sampler whose sampling probability can be updated at runtime

## Component owners

Expand Down
2 changes: 2 additions & 0 deletions consistent-sampling/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ otelJava.moduleName.set("io.opentelemetry.contrib.sampler")
dependencies {
api("io.opentelemetry:opentelemetry-sdk-trace")
api("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
api("io.opentelemetry:opentelemetry-sdk-extension-incubator")

testImplementation("org.hipparchus:hipparchus-core:4.0.3")
testImplementation("org.hipparchus:hipparchus-stat:4.0.3")
}
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,21 @@
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.sampler.consistent56;
package io.opentelemetry.contrib.sampler.consistent;

import static io.opentelemetry.contrib.sampler.consistent56.ConsistentSamplingUtil.getInvalidThreshold;
import static io.opentelemetry.contrib.sampler.consistent56.ConsistentSamplingUtil.isValidThreshold;
import static io.opentelemetry.contrib.sampler.consistent.ConsistentSamplingUtil.getInvalidThreshold;
import static io.opentelemetry.contrib.sampler.consistent.ConsistentSamplingUtil.isValidThreshold;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.TraceState;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.extension.incubator.trace.samplers.ComposableSampler;
import io.opentelemetry.sdk.extension.incubator.trace.samplers.SamplingIntent;
import io.opentelemetry.sdk.trace.data.LinkData;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nullable;
Expand All @@ -25,9 +28,9 @@
* uses the minimum threshold value received.
*/
@Immutable
final class ConsistentAnyOf extends ConsistentSampler {
final class ConsistentAnyOf implements ComposableSampler {

private final Composable[] delegates;
private final ComposableSampler[] delegates;

private final String description;

Expand All @@ -36,7 +39,7 @@ final class ConsistentAnyOf extends ConsistentSampler {
*
* @param delegates the delegate samplers
*/
ConsistentAnyOf(@Nullable Composable... delegates) {
ConsistentAnyOf(@Nullable ComposableSampler... delegates) {
if (delegates == null || delegates.length == 0) {
throw new IllegalArgumentException(
"At least one delegate must be specified for ConsistentAnyOf");
Expand All @@ -53,6 +56,7 @@ final class ConsistentAnyOf extends ConsistentSampler {
@Override
public SamplingIntent getSamplingIntent(
Context parentContext,
String traceId,
String name,
SpanKind spanKind,
Attributes attributes,
Expand All @@ -65,64 +69,54 @@ public SamplingIntent getSamplingIntent(
long minimumThreshold = getInvalidThreshold();

// If any of the delegates returning the threshold value equal to T returns true upon calling
// its IsAdjustedCountReliable() method, the resulting isAdjustedCountReliable is true,
// its isThresholdReliable() method, the resulting isThresholdReliable is true,
// otherwise it is false.
boolean isAdjustedCountCorrect = false;
boolean isThresholdReliable = false;

int k = 0;
for (Composable delegate : delegates) {
for (ComposableSampler delegate : delegates) {
SamplingIntent delegateIntent =
delegate.getSamplingIntent(parentContext, name, spanKind, attributes, parentLinks);
delegate.getSamplingIntent(
parentContext, traceId, name, spanKind, attributes, parentLinks);
long delegateThreshold = delegateIntent.getThreshold();
if (isValidThreshold(delegateThreshold)) {
if (isValidThreshold(minimumThreshold)) {
if (delegateThreshold == minimumThreshold) {
if (delegateIntent.isAdjustedCountReliable()) {
isAdjustedCountCorrect = true;
if (delegateIntent.isThresholdReliable()) {
isThresholdReliable = true;
}
} else if (delegateThreshold < minimumThreshold) {
minimumThreshold = delegateThreshold;
isAdjustedCountCorrect = delegateIntent.isAdjustedCountReliable();
isThresholdReliable = delegateIntent.isThresholdReliable();
}
} else {
minimumThreshold = delegateThreshold;
isAdjustedCountCorrect = delegateIntent.isAdjustedCountReliable();
isThresholdReliable = delegateIntent.isThresholdReliable();
}
}
intents[k++] = delegateIntent;
}

long resultingThreshold = minimumThreshold;
boolean isResultingAdjustedCountCorrect = isAdjustedCountCorrect;
boolean resultingThresholdReliable = isThresholdReliable;

return new SamplingIntent() {
@Override
public long getThreshold() {
return resultingThreshold;
}

@Override
public boolean isAdjustedCountReliable() {
return isResultingAdjustedCountCorrect;
}
AttributesBuilder builder = Attributes.builder();
for (SamplingIntent intent : intents) {
builder = builder.putAll(intent.getAttributes());
}
Attributes mergedAttributes = builder.build();

@Override
public Attributes getAttributes() {
AttributesBuilder builder = Attributes.builder();
for (SamplingIntent intent : intents) {
builder = builder.putAll(intent.getAttributes());
}
return builder.build();
}
Function<TraceState, TraceState> composedUpdater =
previousState -> {
TraceState state = previousState;
for (SamplingIntent intent : intents) {
state = intent.getTraceStateUpdater().apply(state);
}
return state;
};

@Override
public TraceState updateTraceState(TraceState previousState) {
for (SamplingIntent intent : intents) {
previousState = intent.updateTraceState(previousState);
}
return previousState;
}
};
return SamplingIntent.create(
resultingThreshold, resultingThresholdReliable, mergedAttributes, composedUpdater);
}

@Override
Expand Down

This file was deleted.

Loading
Loading