Skip to content

Commit

Permalink
ddtrace/tracer: removed dependency between discovery flag and single …
Browse files Browse the repository at this point in the history
…span sampling

Previously span sampling was executed if stats in the tracer were enabled, that is the "discovery" flag was on.
This dependency was removed and more tests added

Also,

client_dropped_with_single_spans:stats_enabled : span priority <= 0, ( client-stats enabled). Expected result: send only the single span sampled spans
client_dropped_with_single_spans:stats_disabled. span priority <= 0, ( client-stats disabled). Expected result: send everything but mark single span sampled spans
client_dropped_with_single_span_rules tests if behaviour is as expected with sampling rules that don't match spans
client_kept_with_single_spans makes sure we only run on dropped traces
single_spans_with_max_per_second (trace rate: 0.8, span rule: rate:1, max_per_second: 50). Tests that we sample no more than [0.8 + 0.2*1( but max 50 spans)] spans
single_spans_without_max_per_second x2 - testing sampling rates (traceRate : 0.8, spanRate : 0.5)
  • Loading branch information
Julio-Guerra committed Oct 26, 2022
1 parent b1c702e commit 7748843
Show file tree
Hide file tree
Showing 7 changed files with 281 additions and 22 deletions.
2 changes: 1 addition & 1 deletion ddtrace/tracer/rules_sampler.go
Expand Up @@ -360,7 +360,7 @@ func (rs *singleSpanRulesSampler) apply(span *span) bool {
}
var sampled bool
if rule.limiter != nil {
sampled, rate = rule.limiter.allowOne(time.Now())
sampled, rate = rule.limiter.allowOne(nowTime())
if !sampled {
return false
}
Expand Down
41 changes: 40 additions & 1 deletion ddtrace/tracer/sampler_test.go
Expand Up @@ -355,7 +355,6 @@ func TestRulesSampler(t *testing.T) {
makeSpan := func(op string, svc string) *span {
return newSpan(op, svc, "", 0, 0, 0)
}

t.Run("no-rules", func(t *testing.T) {
assert := assert.New(t)
rs := newRulesSampler(nil, nil)
Expand Down Expand Up @@ -449,6 +448,46 @@ func TestRulesSampler(t *testing.T) {
}
})

t.Run("matching-span-rules", func(t *testing.T) {
defer os.Unsetenv("DD_SPAN_SAMPLING_RULES")
for _, tt := range []struct {
rules string
spanSrv string
spanName string
}{
{
rules: `[{"name": "abcd?", "sample_rate": 1.0, "max_per_second":100}]`,
spanSrv: "test-service",
spanName: "abcde",
},
{
rules: `[{"service": "*abcd","max_per_second":100, "sample_rate": 1.0}]`,
spanSrv: "xyzabcd",
spanName: "abcde",
},
{
rules: `[{"service": "?*", "sample_rate": 1.0, "max_per_second":100}]`,
spanSrv: "test-service",
spanName: "abcde",
},
} {
t.Run("", func(t *testing.T) {
os.Setenv("DD_SPAN_SAMPLING_RULES", tt.rules)
_, rules, _ := samplingRulesFromEnv()

assert := assert.New(t)
rs := newRulesSampler(nil, rules)

span := makeFinishedSpan(tt.spanName, tt.spanSrv)
result := rs.SampleSpan(span)
assert.True(result)
assert.Contains(span.Metrics, keySpanSamplingMechanism)
assert.Contains(span.Metrics, keySingleSpanSamplingRuleRate)
assert.Contains(span.Metrics, keySingleSpanSamplingMPS)
})
}
})

t.Run("not-matching-span-rules-from-env", func(t *testing.T) {
defer os.Unsetenv("DD_SPAN_SAMPLING_RULES")
for _, tt := range []struct {
Expand Down
2 changes: 1 addition & 1 deletion ddtrace/tracer/spancontext.go
Expand Up @@ -341,6 +341,6 @@ func (t *trace) finishedOne(s *span) {
atomic.AddUint32(&tr.spansFinished, uint32(len(t.spans)))
tr.pushTrace(&finishedTrace{
spans: t.spans,
decision: samplingDecision(atomic.LoadUint32((*uint32)(&t.samplingDecision))),
willSend: decisionKeep == samplingDecision(atomic.LoadUint32((*uint32)(&t.samplingDecision))),
})
}
7 changes: 4 additions & 3 deletions ddtrace/tracer/time.go
Expand Up @@ -10,7 +10,8 @@ package tracer

import "time"

// nowTime returns the current time, as computed by Time.Now().
var nowTime func() time.Time = func() time.Time { return time.Now() }

// now returns the current UNIX time in nanoseconds, as computed by Time.UnixNano().
func now() int64 {
return time.Now().UnixNano()
}
var now func() int64 = func() int64 { return time.Now().UnixNano() }
22 changes: 15 additions & 7 deletions ddtrace/tracer/time_windows.go
Expand Up @@ -26,15 +26,23 @@ func lowPrecisionNow() int64 {
return time.Now().UnixNano()
}

var now func() int64
// We use this method of initializing now over an init function due to dependency issues. The init
// function may run after other declarations, such as that in payload_test:19, which results in a
// nil dereference panic.
var now func() int64 = func() func() int64 {
if err := windows.LoadGetSystemTimePreciseAsFileTime(); err != nil {
log.Warn("Unable to load high precison timer, defaulting to time.Now()")
return lowPrecisionNow
} else {
return highPrecisionNow
}
}()

// If GetSystemTimePreciseAsFileTime is not available we default to the less
// precise implementation based on time.Now()
func init() {
var nowTime func() time.Time = func() func() time.Time {
if err := windows.LoadGetSystemTimePreciseAsFileTime(); err != nil {
log.Warn("Unable to load high precison timer, defaulting to time.Now()")
now = lowPrecisionNow
return func() time.Time { return time.Unix(0, lowPrecisionNow()) }
} else {
now = highPrecisionNow
return func() time.Time { return time.Unix(0, highPrecisionNow()) }
}
}
}()
13 changes: 9 additions & 4 deletions ddtrace/tracer/tracer.go
Expand Up @@ -323,13 +323,16 @@ func (t *tracer) worker(tick <-chan time.Time) {
// finishedTrace holds information about a trace that has finished, including its spans.
type finishedTrace struct {
spans []*span
decision samplingDecision
willSend bool // willSend indicates whether the trace will be sent to the agent.
}

// sampleFinishedTrace applies single-span sampling to the provided trace, which is considered to be finished.
func (t *tracer) sampleFinishedTrace(info *finishedTrace) {
if info.decision == decisionKeep {
return
if len(info.spans) > 0 {
if p, ok := info.spans[0].context.samplingPriority(); ok && p > 0 {
// The trace is kept, no need to run single span sampling rules.
return
}
}
var kept []*span
if t.rulesSampling.HasSpanRules() {
Expand All @@ -348,7 +351,9 @@ func (t *tracer) sampleFinishedTrace(info *finishedTrace) {
atomic.AddUint32(&t.droppedP0Traces, 1)
}
atomic.AddUint32(&t.droppedP0Spans, uint32(len(info.spans)-len(kept)))
info.spans = kept
if !info.willSend {
info.spans = kept
}
}

func (t *tracer) pushTrace(trace *finishedTrace) {
Expand Down

0 comments on commit 7748843

Please sign in to comment.