Skip to content

Commit e08e673

Browse files
andrejpkyaron2daixiang0dapr-bot
authored
Switch to OTel ParentSampler for correct sampling behavior (dapr#7573)
* Switch to OTel ParentSampler for correct sampling behavior Signed-off-by: Andrej Kyselica <[email protected]> * fix formatting Signed-off-by: Andrej Kyselica <[email protected]> * fix lint error Signed-off-by: Andrej Kyselica <[email protected]> --------- Signed-off-by: Andrej Kyselica <[email protected]> Signed-off-by: Andrej Kyselica <[email protected]> Co-authored-by: Yaron Schneider <[email protected]> Co-authored-by: Loong Dai <[email protected]> Co-authored-by: Dapr Bot <[email protected]>
1 parent 6ffe177 commit e08e673

File tree

2 files changed

+40
-50
lines changed

2 files changed

+40
-50
lines changed

pkg/diagnostics/tracing_sampler.go

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,12 @@
11
package diagnostics
22

33
import (
4-
"fmt"
5-
64
sdktrace "go.opentelemetry.io/otel/sdk/trace"
7-
"go.opentelemetry.io/otel/trace"
85

96
diagUtils "github.com/dapr/dapr/pkg/diagnostics/utils"
107
)
118

12-
type DaprTraceSampler struct {
13-
sdktrace.Sampler
14-
ProbabilitySampler sdktrace.Sampler
15-
SamplingRate float64
16-
}
17-
18-
/**
19-
* Decisions for the Dapr sampler are as follows:
20-
* - parent has sample flag turned on -> sample
21-
* - parent has sample turned off -> fall back to probability-based sampling
22-
*/
23-
func (d *DaprTraceSampler) ShouldSample(p sdktrace.SamplingParameters) sdktrace.SamplingResult {
24-
psc := trace.SpanContextFromContext(p.ParentContext)
25-
if psc.IsValid() && psc.IsSampled() {
26-
// Parent is valid and specifies sampling flag is on -> sample
27-
return sdktrace.AlwaysSample().ShouldSample(p)
28-
}
29-
30-
// Parent is invalid or does not have sampling enabled -> sample probabilistically
31-
return d.ProbabilitySampler.ShouldSample(p)
32-
}
33-
34-
func (d *DaprTraceSampler) Description() string {
35-
return fmt.Sprintf("DaprTraceSampler(P=%f)", d.SamplingRate)
36-
}
37-
38-
func NewDaprTraceSampler(samplingRateString string) *DaprTraceSampler {
9+
func NewDaprTraceSampler(samplingRateString string) sdktrace.Sampler {
3910
samplingRate := diagUtils.GetTraceSamplingRate(samplingRateString)
40-
return &DaprTraceSampler{
41-
SamplingRate: samplingRate,
42-
ProbabilitySampler: sdktrace.TraceIDRatioBased(samplingRate),
43-
}
11+
return sdktrace.ParentBased(sdktrace.TraceIDRatioBased(samplingRate))
4412
}

pkg/diagnostics/tracing_test.go

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -162,27 +162,49 @@ func TestStartInternalCallbackSpan(t *testing.T) {
162162

163163
t.Run("traceparent is provided with sampling flag = 0 and sampling is enabled (but not P=1.00)", func(t *testing.T) {
164164
// We use a fixed seed for the RNG so we can use an exact number here
165-
const expectSampled = 1051
165+
const expectSampled = 0
166166
const numTraces = 100000
167-
sampledCount := runTraces(t, "test_trace", numTraces, "0.01", 0)
167+
sampledCount := runTraces(t, "test_trace", numTraces, "0.01", true, 0)
168168
require.Equal(t, expectSampled, sampledCount, "Expected to sample %d traces but sampled %d", expectSampled, sampledCount)
169169
require.Less(t, sampledCount, numTraces, "Expected to sample fewer than the total number of traces, but sampled all of them!")
170170
})
171171

172172
t.Run("traceparent is provided with sampling flag = 0 and sampling is enabled (and P=1.00)", func(t *testing.T) {
173+
const expectSampled = 0
173174
const numTraces = 1000
174-
sampledCount := runTraces(t, "test_trace", numTraces, "1.00", 0)
175-
require.Equal(t, numTraces, sampledCount, "Expected to sample all traces (%d) but only sampled %d", numTraces, sampledCount)
175+
sampledCount := runTraces(t, "test_trace", numTraces, "1.00", true, 0)
176+
require.Equal(t, expectSampled, sampledCount, "Expected to sample all traces (%d) but only sampled %d", numTraces, sampledCount)
176177
})
177178

178179
t.Run("traceparent is provided with sampling flag = 1 and sampling is enabled (but not P=1.00)", func(t *testing.T) {
179180
const numTraces = 1000
180-
sampledCount := runTraces(t, "test_trace", numTraces, "0.00001", 1)
181+
sampledCount := runTraces(t, "test_trace", numTraces, "0.00001", true, 1)
182+
require.Equal(t, numTraces, sampledCount, "Expected to sample all traces (%d) but only sampled %d", numTraces, sampledCount)
183+
})
184+
185+
t.Run("traceparent is not provided and sampling is enabled (but not P=1.00)", func(t *testing.T) {
186+
// We use a fixed seed for the RNG so we can use an exact number here
187+
const expectSampled = 1000 // we allow for a 10% margin of error to account for randomness
188+
const numTraces = 100000
189+
sampledCount := runTraces(t, "test_trace", numTraces, "0.01", false, 0)
190+
require.InEpsilon(t, expectSampled, sampledCount, 0.1, "Expected to sample %d (+/- 10%) traces but sampled %d", expectSampled, sampledCount)
191+
require.Less(t, sampledCount, numTraces, "Expected to sample fewer than the total number of traces, but sampled all of them!")
192+
})
193+
194+
t.Run("traceparent is not provided and sampling is enabled (and P=1.00)", func(t *testing.T) {
195+
const numTraces = 1000
196+
sampledCount := runTraces(t, "test_trace", numTraces, "1.00", false, 0)
181197
require.Equal(t, numTraces, sampledCount, "Expected to sample all traces (%d) but only sampled %d", numTraces, sampledCount)
182198
})
199+
200+
t.Run("traceparent is not provided and sampling is enabled (but almost 0 P=0.00001)", func(t *testing.T) {
201+
const numTraces = 1000
202+
sampledCount := runTraces(t, "test_trace", numTraces, "0.00001", false, 0)
203+
require.Less(t, sampledCount, int(numTraces*.001), "Expected to sample no traces (+/- 10%) but only sampled %d", sampledCount)
204+
})
183205
}
184206

185-
func runTraces(t *testing.T, testName string, numTraces int, samplingRate string, parentTraceFlag int) int {
207+
func runTraces(t *testing.T, testName string, numTraces int, samplingRate string, hasParentSpanContext bool, parentTraceFlag int) int {
186208
d := NewDaprTraceSampler(samplingRate)
187209
tracerOptions := []sdktrace.TracerProviderOption{
188210
sdktrace.WithSampler(d),
@@ -199,17 +221,17 @@ func runTraces(t *testing.T, testName string, numTraces int, samplingRate string
199221
sampledCount := 0
200222

201223
for i := 0; i < numTraces; i++ {
202-
traceID, _ := idg.NewIDs(context.Background())
203-
scConfig := trace.SpanContextConfig{
204-
TraceID: traceID,
205-
SpanID: trace.SpanID{0, 240, 103, 170, 11, 169, 2, 183},
206-
TraceFlags: trace.TraceFlags(parentTraceFlag),
207-
}
208-
209-
parent := trace.NewSpanContext(scConfig)
210-
211224
ctx := context.Background()
212-
ctx = trace.ContextWithRemoteSpanContext(ctx, parent)
225+
if hasParentSpanContext {
226+
traceID, _ := idg.NewIDs(context.Background())
227+
scConfig := trace.SpanContextConfig{
228+
TraceID: traceID,
229+
SpanID: trace.SpanID{0, 240, 103, 170, 11, 169, 2, 183},
230+
TraceFlags: trace.TraceFlags(parentTraceFlag),
231+
}
232+
parent := trace.NewSpanContext(scConfig)
233+
ctx = trace.ContextWithRemoteSpanContext(ctx, parent)
234+
}
213235
ctx, span := testTracer.Start(ctx, "testTraceSpan", trace.WithSpanKind(trace.SpanKindClient))
214236
assert.NotNil(t, span)
215237
assert.NotNil(t, ctx)

0 commit comments

Comments
 (0)