Skip to content
This repository was archived by the owner on Dec 23, 2023. It is now read-only.

Commit 23d4980

Browse files
author
Bogdan Drutu
authored
Fix probability sampler and sampling. (#661)
* Fix probability sampler and sampling. * Add more tests and fix comments.
1 parent de9f67c commit 23d4980

File tree

6 files changed

+286
-36
lines changed

6 files changed

+286
-36
lines changed

api/src/main/java/io/opencensus/trace/samplers/ProbabilitySampler.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ abstract class ProbabilitySampler extends Sampler {
5656
static ProbabilitySampler create(double probability) {
5757
checkArgument(
5858
probability >= 0.0 && probability <= 1.0, "probability must be in range [0.0, 1.0]");
59-
long idUpperBound = 0;
59+
long idUpperBound;
6060
// Special case the limits, to avoid any possible issues with lack of precision across
6161
// double/long boundaries. For probability == 0.0, we use Long.MIN_VALUE as this guarantees
6262
// that we will never sample a trace, even in the case where the id == Long.MIN_VALUE, since
@@ -79,6 +79,16 @@ public final boolean shouldSample(
7979
SpanId spanId,
8080
String name,
8181
@Nullable List<Span> parentLinks) {
82+
// If the parent is sampled keep the sampling decision.
83+
if (parentContext != null && parentContext.getTraceOptions().isSampled()) {
84+
return true;
85+
}
86+
// If any parent link is sampled keep the sampling decision.
87+
for (Span parentLink : parentLinks) {
88+
if (parentLink.getContext().getTraceOptions().isSampled()) {
89+
return true;
90+
}
91+
}
8292
// Always sample if we are within probability range. This is true even for child spans (that
8393
// may have had a different sampling decision made) to allow for different sampling policies,
8494
// and dynamic increases to sampling probabilities for debugging purposes.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2017, OpenCensus Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.opencensus.trace;
18+
19+
import java.util.EnumSet;
20+
import java.util.Map;
21+
import javax.annotation.Nullable;
22+
23+
/**
24+
* Class to be used in tests where an implementation for the Span is needed.
25+
*
26+
* <p>Not final to allow Mockito to "spy" this class.
27+
*/
28+
public class NoopSpan extends Span {
29+
30+
/** Creates a new {@code NoopSpan}. */
31+
public NoopSpan(SpanContext context, @Nullable EnumSet<Options> options) {
32+
super(context, options);
33+
}
34+
35+
@Override
36+
public void putAttributes(Map<String, AttributeValue> attributes) {}
37+
38+
@Override
39+
public void addAnnotation(String description, Map<String, AttributeValue> attributes) {}
40+
41+
@Override
42+
public void addAnnotation(Annotation annotation) {}
43+
44+
@Override
45+
public void addNetworkEvent(NetworkEvent networkEvent) {}
46+
47+
@Override
48+
public void addLink(Link link) {}
49+
50+
@Override
51+
public void end(EndSpanOptions options) {}
52+
}

api/src/test/java/io/opencensus/trace/SpanTest.java

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323

2424
import java.util.Collections;
2525
import java.util.EnumSet;
26-
import java.util.Map;
2726
import java.util.Random;
2827
import org.junit.Before;
2928
import org.junit.Test;
@@ -99,29 +98,4 @@ public void endCallsEndWithDefaultOptions() {
9998
span.end();
10099
verify(span).end(same(EndSpanOptions.DEFAULT));
101100
}
102-
103-
// No-op implementation of the Span for testing only.
104-
private static class NoopSpan extends Span {
105-
private NoopSpan(SpanContext context, EnumSet<Span.Options> options) {
106-
super(context, options);
107-
}
108-
109-
@Override
110-
public void putAttributes(Map<String, AttributeValue> attributes) {}
111-
112-
@Override
113-
public void addAnnotation(String description, Map<String, AttributeValue> attributes) {}
114-
115-
@Override
116-
public void addAnnotation(Annotation annotation) {}
117-
118-
@Override
119-
public void addNetworkEvent(NetworkEvent networkEvent) {}
120-
121-
@Override
122-
public void addLink(Link link) {}
123-
124-
@Override
125-
public void end(EndSpanOptions options) {}
126-
}
127101
}

api/src/test/java/io/opencensus/trace/samplers/SamplersTest.java

Lines changed: 130 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,17 @@
1818

1919
import static com.google.common.truth.Truth.assertThat;
2020

21+
import io.opencensus.trace.NoopSpan;
2122
import io.opencensus.trace.Sampler;
2223
import io.opencensus.trace.Span;
2324
import io.opencensus.trace.SpanContext;
2425
import io.opencensus.trace.SpanId;
2526
import io.opencensus.trace.TraceId;
2627
import io.opencensus.trace.TraceOptions;
28+
import java.util.Arrays;
2729
import java.util.Collections;
30+
import java.util.EnumSet;
31+
import java.util.List;
2832
import java.util.Random;
2933
import org.junit.Test;
3034
import org.junit.runner.RunWith;
@@ -43,6 +47,8 @@ public class SamplersTest {
4347
SpanContext.create(traceId, parentSpanId, TraceOptions.builder().setIsSampled(true).build());
4448
private final SpanContext notSampledSpanContext =
4549
SpanContext.create(traceId, parentSpanId, TraceOptions.DEFAULT);
50+
private final Span sampledSpan =
51+
new NoopSpan(sampledSpanContext, EnumSet.of(Span.Options.RECORD_EVENTS));
4652

4753
@Test
4854
public void alwaysSampleSampler_AlwaysReturnTrue() {
@@ -118,7 +124,7 @@ public void probabilitySampler_outOfRangeLowProbability() {
118124

119125
// Applies the given sampler to NUM_SAMPLE_TRIES random traceId/spanId pairs.
120126
private static void assertSamplerSamplesWithProbability(
121-
Sampler sampler, SpanContext parent, double probability) {
127+
Sampler sampler, SpanContext parent, List<Span> parentLinks, double probability) {
122128
Random random = new Random(1234);
123129
int count = 0; // Count of spans with sampling enabled
124130
for (int i = 0; i < NUM_SAMPLE_TRIES; i++) {
@@ -128,7 +134,7 @@ private static void assertSamplerSamplesWithProbability(
128134
TraceId.generateRandomId(random),
129135
SpanId.generateRandomId(random),
130136
SPAN_NAME,
131-
Collections.<Span>emptyList())) {
137+
parentLinks)) {
132138
count++;
133139
}
134140
}
@@ -139,17 +145,133 @@ private static void assertSamplerSamplesWithProbability(
139145
}
140146

141147
@Test
142-
public void probabilitySampler_differentProbabilities() {
148+
public void probabilitySampler_DifferentProbabilities_NotSampledParent() {
143149
final Sampler neverSample = Samplers.probabilitySampler(0.0);
144-
assertSamplerSamplesWithProbability(neverSample, sampledSpanContext, 0.0);
150+
assertSamplerSamplesWithProbability(
151+
neverSample, notSampledSpanContext, Collections.<Span>emptyList(), 0.0);
145152
final Sampler alwaysSample = Samplers.probabilitySampler(1.0);
146-
assertSamplerSamplesWithProbability(alwaysSample, sampledSpanContext, 1.0);
153+
assertSamplerSamplesWithProbability(
154+
alwaysSample, notSampledSpanContext, Collections.<Span>emptyList(), 1.0);
147155
final Sampler fiftyPercentSample = Samplers.probabilitySampler(0.5);
148-
assertSamplerSamplesWithProbability(fiftyPercentSample, sampledSpanContext, 0.5);
156+
assertSamplerSamplesWithProbability(
157+
fiftyPercentSample, notSampledSpanContext, Collections.<Span>emptyList(), 0.5);
149158
final Sampler twentyPercentSample = Samplers.probabilitySampler(0.2);
150-
assertSamplerSamplesWithProbability(twentyPercentSample, sampledSpanContext, 0.2);
159+
assertSamplerSamplesWithProbability(
160+
twentyPercentSample, notSampledSpanContext, Collections.<Span>emptyList(), 0.2);
151161
final Sampler twoThirdsSample = Samplers.probabilitySampler(2.0 / 3.0);
152-
assertSamplerSamplesWithProbability(twoThirdsSample, sampledSpanContext, 2.0 / 3.0);
162+
assertSamplerSamplesWithProbability(
163+
twoThirdsSample, notSampledSpanContext, Collections.<Span>emptyList(), 2.0 / 3.0);
164+
}
165+
166+
@Test
167+
public void probabilitySampler_DifferentProbabilities_SampledParent() {
168+
final Sampler neverSample = Samplers.probabilitySampler(0.0);
169+
assertSamplerSamplesWithProbability(
170+
neverSample, sampledSpanContext, Collections.<Span>emptyList(), 1.0);
171+
final Sampler alwaysSample = Samplers.probabilitySampler(1.0);
172+
assertSamplerSamplesWithProbability(
173+
alwaysSample, sampledSpanContext, Collections.<Span>emptyList(), 1.0);
174+
final Sampler fiftyPercentSample = Samplers.probabilitySampler(0.5);
175+
assertSamplerSamplesWithProbability(
176+
fiftyPercentSample, sampledSpanContext, Collections.<Span>emptyList(), 1.0);
177+
final Sampler twentyPercentSample = Samplers.probabilitySampler(0.2);
178+
assertSamplerSamplesWithProbability(
179+
twentyPercentSample, sampledSpanContext, Collections.<Span>emptyList(), 1.0);
180+
final Sampler twoThirdsSample = Samplers.probabilitySampler(2.0 / 3.0);
181+
assertSamplerSamplesWithProbability(
182+
twoThirdsSample, sampledSpanContext, Collections.<Span>emptyList(), 1.0);
183+
}
184+
185+
@Test
186+
public void probabilitySampler_DifferentProbabilities_SampledParentLink() {
187+
final Sampler neverSample = Samplers.probabilitySampler(0.0);
188+
assertSamplerSamplesWithProbability(
189+
neverSample, notSampledSpanContext, Arrays.asList(sampledSpan), 1.0);
190+
final Sampler alwaysSample = Samplers.probabilitySampler(1.0);
191+
assertSamplerSamplesWithProbability(
192+
alwaysSample, notSampledSpanContext, Arrays.asList(sampledSpan), 1.0);
193+
final Sampler fiftyPercentSample = Samplers.probabilitySampler(0.5);
194+
assertSamplerSamplesWithProbability(
195+
fiftyPercentSample, notSampledSpanContext, Arrays.asList(sampledSpan), 1.0);
196+
final Sampler twentyPercentSample = Samplers.probabilitySampler(0.2);
197+
assertSamplerSamplesWithProbability(
198+
twentyPercentSample, notSampledSpanContext, Arrays.asList(sampledSpan), 1.0);
199+
final Sampler twoThirdsSample = Samplers.probabilitySampler(2.0 / 3.0);
200+
assertSamplerSamplesWithProbability(
201+
twoThirdsSample, notSampledSpanContext, Arrays.asList(sampledSpan), 1.0);
202+
}
203+
204+
@Test
205+
public void probabilitySampler_SampleBasedOnTraceId() {
206+
final Sampler defaultProbability = Samplers.probabilitySampler(0.0001);
207+
// This traceId will not be sampled by the ProbabilitySampler because the first 8 bytes as long
208+
// is not less than probability * Long.MAX_VALUE;
209+
TraceId notSampledtraceId =
210+
TraceId.fromBytes(
211+
new byte[] {
212+
(byte) 0x8F,
213+
(byte) 0xFF,
214+
(byte) 0xFF,
215+
(byte) 0xFF,
216+
(byte) 0xFF,
217+
(byte) 0xFF,
218+
(byte) 0xFF,
219+
(byte) 0xFF,
220+
0,
221+
0,
222+
0,
223+
0,
224+
0,
225+
0,
226+
0,
227+
0
228+
});
229+
assertThat(
230+
defaultProbability.shouldSample(
231+
null,
232+
false,
233+
notSampledtraceId,
234+
SpanId.generateRandomId(random),
235+
SPAN_NAME,
236+
Collections.<Span>emptyList()))
237+
.isFalse();
238+
// This traceId will be sampled by the ProbabilitySampler because the first 8 bytes as long
239+
// is less than probability * Long.MAX_VALUE;
240+
TraceId sampledtraceId =
241+
TraceId.fromBytes(
242+
new byte[] {
243+
(byte) 0x00,
244+
(byte) 0x00,
245+
(byte) 0xFF,
246+
(byte) 0xFF,
247+
(byte) 0xFF,
248+
(byte) 0xFF,
249+
(byte) 0xFF,
250+
(byte) 0xFF,
251+
0,
252+
0,
253+
0,
254+
0,
255+
0,
256+
0,
257+
0,
258+
0
259+
});
260+
assertThat(
261+
defaultProbability.shouldSample(
262+
null,
263+
false,
264+
sampledtraceId,
265+
SpanId.generateRandomId(random),
266+
SPAN_NAME,
267+
Collections.<Span>emptyList()))
268+
.isTrue();
269+
}
270+
271+
@Test
272+
public void probabilitySampler_getDescription() {
273+
assertThat((Samplers.probabilitySampler(0.5)).getDescription())
274+
.isEqualTo("ProbabilitySampler{0.500000}");
153275
}
154276

155277
@Test

impl_core/src/main/java/io/opencensus/implcore/trace/SpanBuilderImpl.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,16 @@ private static boolean makeSamplingDecision(
118118
.shouldSample(parent, hasRemoteParent, traceId, spanId, name, parentLinks);
119119
}
120120
// Parent is always different than null because otherwise we use the default sampler.
121-
return parent.getTraceOptions().isSampled();
121+
return parent.getTraceOptions().isSampled() || isAnyParentLinkSampled(parentLinks);
122+
}
123+
124+
private static boolean isAnyParentLinkSampled(List<Span> parentLinks) {
125+
for (Span parentLink : parentLinks) {
126+
if (parentLink.getContext().getTraceOptions().isSampled()) {
127+
return true;
128+
}
129+
}
130+
return false;
122131
}
123132

124133
private static void linkSpans(Span span, List<Span> parentLinks) {

0 commit comments

Comments
 (0)