Skip to content

Commit

Permalink
ddtrace/tracer: optimize baggage item handling. (#611)
Browse files Browse the repository at this point in the history
When creating a span, ForEachBaggageItem is called, which acquires a
mutex to check for and copy baggage items. Often no baggage is present,
and the expensive mutex Lock() and Unlock() are a waste. This commit
adds an atomic int32 which is set whenever the first baggage item is set,
and checked in ForEachBaggageItem to see if any baggage is present
before doing the more expensive mutex Lock(). It also checks the int
before locking in the baggageItem() function.
  • Loading branch information
knusbaum authored Mar 18, 2020
1 parent 2038a27 commit 876b6b3
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 11 deletions.
14 changes: 11 additions & 3 deletions ddtrace/tracer/spancontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ type spanContext struct {
traceID uint64
spanID uint64

mu sync.RWMutex // guards below fields
baggage map[string]string
origin string // e.g. "synthetics"
mu sync.RWMutex // guards below fields
baggage map[string]string
hasBaggage int32 // atomic int for quick checking presence of baggage. 0 indicates no baggage, otherwise baggage exists.
origin string // e.g. "synthetics"
}

// newSpanContext creates a new SpanContext to serve as context for the given
Expand Down Expand Up @@ -77,6 +78,9 @@ func (c *spanContext) TraceID() uint64 { return c.traceID }

// ForeachBaggageItem implements ddtrace.SpanContext.
func (c *spanContext) ForeachBaggageItem(handler func(k, v string) bool) {
if atomic.LoadInt32(&c.hasBaggage) == 0 {
return
}
c.mu.RLock()
defer c.mu.RUnlock()
for k, v := range c.baggage {
Expand Down Expand Up @@ -104,12 +108,16 @@ func (c *spanContext) setBaggageItem(key, val string) {
c.mu.Lock()
defer c.mu.Unlock()
if c.baggage == nil {
atomic.StoreInt32(&c.hasBaggage, 1)
c.baggage = make(map[string]string, 1)
}
c.baggage[key] = val
}

func (c *spanContext) baggageItem(key string) string {
if atomic.LoadInt32(&c.hasBaggage) == 0 {
return ""
}
c.mu.RLock()
defer c.mu.RUnlock()
return c.baggage[key]
Expand Down
36 changes: 28 additions & 8 deletions ddtrace/tracer/spancontext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,15 +301,17 @@ func TestSpanContextParent(t *testing.T) {
}
for name, parentCtx := range map[string]*spanContext{
"basic": &spanContext{
baggage: map[string]string{"A": "A", "B": "B"},
trace: newTrace(),
drop: true,
baggage: map[string]string{"A": "A", "B": "B"},
hasBaggage: 1,
trace: newTrace(),
drop: true,
},
"nil-trace": &spanContext{
drop: true,
},
"priority": &spanContext{
baggage: map[string]string{"A": "A", "B": "B"},
baggage: map[string]string{"A": "A", "B": "B"},
hasBaggage: 1,
trace: &trace{
spans: []*span{newBasicSpan("abc")},
priority: func() *float64 { v := new(float64); *v = 2; return v }(),
Expand All @@ -333,9 +335,9 @@ func TestSpanContextParent(t *testing.T) {
if parentCtx.trace != nil {
assert.Equal(ctx.trace.priority, parentCtx.trace.priority)
}
assert.Equal(ctx.drop, parentCtx.drop)
assert.Equal(ctx.baggage, parentCtx.baggage)
assert.Equal(ctx.origin, parentCtx.origin)
assert.Equal(parentCtx.drop, ctx.drop)
assert.Equal(parentCtx.baggage, ctx.baggage)
assert.Equal(parentCtx.origin, ctx.origin)
})
}
}
Expand Down Expand Up @@ -376,7 +378,7 @@ func TestSpanContextIterator(t *testing.T) {
assert := assert.New(t)

got := make(map[string]string)
ctx := spanContext{baggage: map[string]string{"key": "value"}}
ctx := spanContext{baggage: map[string]string{"key": "value"}, hasBaggage: 1}
ctx.ForeachBaggageItem(func(k, v string) bool {
got[k] = v
return true
Expand Down Expand Up @@ -422,3 +424,21 @@ func (tp *testLogger) Reset() {
defer tp.mu.Unlock()
tp.lines = tp.lines[:0]
}

func BenchmarkBaggageItemPresent(b *testing.B) {
ctx := spanContext{baggage: map[string]string{"key": "value"}, hasBaggage: 1}
for n := 0; n < b.N; n++ {
ctx.ForeachBaggageItem(func(k, v string) bool {
return true
})
}
}

func BenchmarkBaggageItemEmpty(b *testing.B) {
ctx := spanContext{}
for n := 0; n < b.N; n++ {
ctx.ForeachBaggageItem(func(k, v string) bool {
return true
})
}
}

0 comments on commit 876b6b3

Please sign in to comment.