Skip to content

Commit 4fdd552

Browse files
authored
Track context containing span in recordingSpan (#7354)
Avoid the allocation of re-embedding the span in a context on end by keeping the one created on start in the span. ### Benchmarks ``` goos: linux goarch: amd64 pkg: go.opentelemetry.io/otel/sdk/trace cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz │ main.out │ trace-recording-span-ctx.out │ │ sec/op │ sec/op vs base │ SpanEnd/ObservabilityEnabled-8 248.2n ± 5% 186.2n ± 4% -24.94% (p=0.000 n=20) TraceStart/ObservabilityEnabled-8 935.8n ± 2% 995.9n ± 4% +6.43% (p=0.000 n=20) geomean 481.9n 430.7n -10.62% │ main.out │ trace-recording-span-ctx.out │ │ B/op │ B/op vs base │ SpanEnd/ObservabilityEnabled-8 64.00 ± 0% 16.00 ± 0% -75.00% (p=0.000 n=20) TraceStart/ObservabilityEnabled-8 608.0 ± 0% 608.0 ± 0% ~ (p=1.000 n=20) ¹ geomean 197.3 98.63 -50.00% ¹ all samples are equal │ main.out │ trace-recording-span-ctx.out │ │ allocs/op │ allocs/op vs base │ SpanEnd/ObservabilityEnabled-8 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.000 n=20) TraceStart/ObservabilityEnabled-8 5.000 ± 0% 5.000 ± 0% ~ (p=1.000 n=20) ¹ geomean 3.162 2.236 -29.29% ¹ all samples are equal ```
1 parent 59563f7 commit 4fdd552

File tree

3 files changed

+29
-14
lines changed

3 files changed

+29
-14
lines changed

sdk/trace/span.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,23 @@ type recordingSpan struct {
151151

152152
// tracer is the SDK tracer that created this span.
153153
tracer *tracer
154+
155+
// origCtx is the context used when starting this span that has the
156+
// recordingSpan instance set as the active span. If not nil, it is used
157+
// when ending the span to ensure any metrics are recorded with a context
158+
// containing this span without requiring an additional allocation.
159+
origCtx context.Context
154160
}
155161

156162
var (
157163
_ ReadWriteSpan = (*recordingSpan)(nil)
158164
_ runtimeTracer = (*recordingSpan)(nil)
159165
)
160166

167+
func (s *recordingSpan) setOrigCtx(ctx context.Context) {
168+
s.origCtx = ctx
169+
}
170+
161171
// SpanContext returns the SpanContext of this span.
162172
func (s *recordingSpan) SpanContext() trace.SpanContext {
163173
if s == nil {
@@ -497,13 +507,17 @@ func (s *recordingSpan) End(options ...trace.SpanEndOption) {
497507
s.mu.Unlock()
498508

499509
if s.tracer.observabilityEnabled {
500-
defer func() {
501-
// Add the span to the context to ensure the metric is recorded
502-
// with the correct span context.
503-
ctx := trace.ContextWithSpan(context.Background(), s)
510+
ctx := s.origCtx
511+
if ctx == nil {
512+
// This should not happen as the origCtx should be set, but
513+
// ensure trace information is propagated in the case of an
514+
// error.
515+
ctx = trace.ContextWithSpan(context.Background(), s)
516+
}
517+
defer func(ctx context.Context) {
504518
set := spanLiveSet(s.spanContext.IsSampled())
505519
s.tracer.spanLiveMetric.AddSet(ctx, -1, set)
506-
}()
520+
}(ctx)
507521
}
508522

509523
sps := s.tracer.provider.getSpanProcessors()

sdk/trace/trace_test.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2780,15 +2780,8 @@ func TestObservabilityContextPropagation(t *testing.T) {
27802780
isValid := s.SpanContext().IsValid()
27812781
assert.True(t, isValid, "Context should have a valid span")
27822782

2783-
if s.IsRecording() {
2784-
// Check if the context value is propagated correctly for Span
2785-
// starts. The Span end operation does not receive any user
2786-
// context so do not check this if the span is not recording
2787-
// (i.e. end operation).
2788-
2789-
got := ctx.Value(ctxKey)
2790-
assert.Equal(t, want, got, "Context value not propagated")
2791-
}
2783+
got := ctx.Value(ctxKey)
2784+
assert.Equal(t, want, got, "Context value not propagated")
27922785
}
27932786
n <- count
27942787
}()

sdk/trace/tracer.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ func (tr *tracer) Start(
5454
s := tr.newSpan(ctx, name, &config)
5555
newCtx := trace.ContextWithSpan(ctx, s)
5656
if tr.observabilityEnabled {
57+
if o, ok := s.(interface{ setOrigCtx(context.Context) }); ok {
58+
// If this is a recording span, store the original context.
59+
// This allows later retrieval of baggage and other information
60+
// that may have been stored in the context at span start time and
61+
// to avoid the allocation of repeatedly calling
62+
// trace.ContextWithSpan.
63+
o.setOrigCtx(newCtx)
64+
}
5765
psc := trace.SpanContextFromContext(ctx)
5866
set := spanStartedSet(psc, s)
5967
tr.spanStartedMetric.AddSet(newCtx, 1, set)

0 commit comments

Comments
 (0)