Skip to content

Commit

Permalink
Merge branch 'master' into feat/single-profiler
Browse files Browse the repository at this point in the history
  • Loading branch information
vaind committed Jul 5, 2023
2 parents c7b2c7c + 6db2fc7 commit 60611ce
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 25 deletions.
55 changes: 31 additions & 24 deletions tracing.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ type Span struct { //nolint: maligned // prefer readability over optimal memory
contexts map[string]Context
// collectProfile is a function that collects a profile of the current transaction. May be nil.
collectProfile transactionProfiler
// a Once instance to make sure that Finish() is only called once.
finishOnce sync.Once
}

// TraceParentContext describes the context of a (remote) parent span.
Expand Down Expand Up @@ -196,31 +198,11 @@ func StartSpan(ctx context.Context, operation string, options ...SpanOption) *Sp

// Finish sets the span's end time, unless already set. If the span is the root
// of a span tree, Finish sends the span tree to Sentry as a transaction.
//
// The logic is executed at most once per span, so that (incorrectly) calling it twice
// never double sends to Sentry.
func (s *Span) Finish() {
// TODO(tracing): maybe make Finish run at most once, such that
// (incorrectly) calling it twice never double sends to Sentry.

if s.EndTime.IsZero() {
s.EndTime = monotonicTimeSince(s.StartTime)
}

if !s.Sampled.Bool() {
return
}
event := s.toEvent()
if event == nil {
return
}

if s.collectProfile != nil {
event.sdkMetaData.transactionProfile = s.collectProfile(s)
}

// TODO(tracing): add breadcrumbs
// (see https://github.com/getsentry/sentry-python/blob/f6f3525f8812f609/sentry_sdk/tracing.py#L372)

hub := hubFromContext(s.ctx)
hub.CaptureEvent(event)
s.finishOnce.Do(s.doFinish)
}

// Context returns the context containing the span.
Expand Down Expand Up @@ -349,6 +331,31 @@ func (s *Span) SetDynamicSamplingContext(dsc DynamicSamplingContext) {
}
}

// doFinish runs the actual Span.Finish() logic.
func (s *Span) doFinish() {
if s.EndTime.IsZero() {
s.EndTime = monotonicTimeSince(s.StartTime)
}

if !s.Sampled.Bool() {
return
}
event := s.toEvent()
if event == nil {
return
}

if s.collectProfile != nil {
event.sdkMetaData.transactionProfile = s.collectProfile(s)
}

// TODO(tracing): add breadcrumbs
// (see https://github.com/getsentry/sentry-python/blob/f6f3525f8812f609/sentry_sdk/tracing.py#L372)

hub := hubFromContext(s.ctx)
hub.CaptureEvent(event)
}

// sentryTracePattern matches either
//
// TRACE_ID - SPAN_ID
Expand Down
25 changes: 25 additions & 0 deletions tracing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -980,3 +980,28 @@ func TestAdjustingTransactionSourceBeforeSending(t *testing.T) {
})
}
}

// This is a regression test for https://github.com/getsentry/sentry-go/issues/587
// Without the "spans can be finished only once" fix, this test will fail
// when run with race detection ("-race").
func TestSpanFinishConcurrentlyWithoutRaces(t *testing.T) {
ctx := NewTestContext(ClientOptions{
EnableTracing: true,
TracesSampleRate: 1,
})
transaction := StartTransaction(ctx, "op")

go func() {
for {
transaction.Finish()
}
}()

go func() {
for {
transaction.Finish()
}
}()

time.Sleep(50 * time.Millisecond)
}
2 changes: 1 addition & 1 deletion transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ func TestHTTPTransport(t *testing.T) {
transportMustFlush := func(t *testing.T, id string) {
t.Helper()

ok := transport.Flush(100 * time.Millisecond)
ok := transport.Flush(500 * time.Millisecond)
if !ok {
t.Fatalf("[CLIENT] {%.4s} Flush() timed out", id)
}
Expand Down

0 comments on commit 60611ce

Please sign in to comment.