1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-09-16 09:26:25 +02:00

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
```
This commit is contained in:
Tyler Yahn
2025-09-15 10:54:12 -07:00
committed by GitHub
parent 59563f7ae4
commit 4fdd552782
3 changed files with 29 additions and 14 deletions

View File

@@ -151,6 +151,12 @@ type recordingSpan struct {
// tracer is the SDK tracer that created this span.
tracer *tracer
// origCtx is the context used when starting this span that has the
// recordingSpan instance set as the active span. If not nil, it is used
// when ending the span to ensure any metrics are recorded with a context
// containing this span without requiring an additional allocation.
origCtx context.Context
}
var (
@@ -158,6 +164,10 @@ var (
_ runtimeTracer = (*recordingSpan)(nil)
)
func (s *recordingSpan) setOrigCtx(ctx context.Context) {
s.origCtx = ctx
}
// SpanContext returns the SpanContext of this span.
func (s *recordingSpan) SpanContext() trace.SpanContext {
if s == nil {
@@ -497,13 +507,17 @@ func (s *recordingSpan) End(options ...trace.SpanEndOption) {
s.mu.Unlock()
if s.tracer.observabilityEnabled {
defer func() {
// Add the span to the context to ensure the metric is recorded
// with the correct span context.
ctx := trace.ContextWithSpan(context.Background(), s)
ctx := s.origCtx
if ctx == nil {
// This should not happen as the origCtx should be set, but
// ensure trace information is propagated in the case of an
// error.
ctx = trace.ContextWithSpan(context.Background(), s)
}
defer func(ctx context.Context) {
set := spanLiveSet(s.spanContext.IsSampled())
s.tracer.spanLiveMetric.AddSet(ctx, -1, set)
}()
}(ctx)
}
sps := s.tracer.provider.getSpanProcessors()

View File

@@ -2780,15 +2780,8 @@ func TestObservabilityContextPropagation(t *testing.T) {
isValid := s.SpanContext().IsValid()
assert.True(t, isValid, "Context should have a valid span")
if s.IsRecording() {
// Check if the context value is propagated correctly for Span
// starts. The Span end operation does not receive any user
// context so do not check this if the span is not recording
// (i.e. end operation).
got := ctx.Value(ctxKey)
assert.Equal(t, want, got, "Context value not propagated")
}
got := ctx.Value(ctxKey)
assert.Equal(t, want, got, "Context value not propagated")
}
n <- count
}()

View File

@@ -54,6 +54,14 @@ func (tr *tracer) Start(
s := tr.newSpan(ctx, name, &config)
newCtx := trace.ContextWithSpan(ctx, s)
if tr.observabilityEnabled {
if o, ok := s.(interface{ setOrigCtx(context.Context) }); ok {
// If this is a recording span, store the original context.
// This allows later retrieval of baggage and other information
// that may have been stored in the context at span start time and
// to avoid the allocation of repeatedly calling
// trace.ContextWithSpan.
o.setOrigCtx(newCtx)
}
psc := trace.SpanContextFromContext(ctx)
set := spanStartedSet(psc, s)
tr.spanStartedMetric.AddSet(newCtx, 1, set)