Skip to content

tracing: centralized span registry #61407

@irfansharif

Description

@irfansharif

Describe the problem

The registry of inflight spans today only captures root spans. We construct an in-memory tree of child spans that derive from the parent spans, and are thus only accessible through tree traversal. This makes a few things more cumbersome, compared to the alternative (below).

// activeSpans is a map that references all non-Finish'ed local root spans,
// i.e. those for which no WithLocalParent(<non-nil>) option was supplied.
// It also elides spans created using WithBypassRegistry.
// The map is keyed on the span ID, which is deterministically unique.
//
// In normal operation, a local root Span is inserted on creation and
// removed on .Finish().
//
// The map can be introspected by `Tracer.VisitSpans`. A Span can also be
// retrieved from its ID by `Tracer.GetActiveSpanFromID`.
activeSpans struct {
// NB: it might be tempting to use a sync.Map here, but
// this incurs an allocation per Span (sync.Map does
// not use a sync.Pool for its internal *entry type).
//
// The bare map approach is essentially allocation-free once the map
// has grown to accommodate the usual number of active local root spans,
// and the critical sections of the mutex are very small.
syncutil.Mutex
m map[uint64]*Span
}

// children contains the list of child spans started after this Span
// started recording.
children []*crdbSpan

Proposed improvement

We can centralize the storage of spans by storing them directly in the same in-memory registry (keyed by Span ID) and have parent spans only refer to child spans by IDs, rather than direct pointers. See #59310 for a prototype of what that could look like.

It comes with a few upsides.

  • Memory accounting: we'll have a centralized place for where it all happens, instead of threading it in through individual spans and child spans. It also lets us introduce a global debug switch to drop all structured recordings we’re holding onto, or control what the RSS is for the current set of observable structured events.
  • Historical introspection: by decoupling the garbage collection of structured events from the lifecycle of the span itself, we can go back in time (very briefly) and see what the collected events were. Right now we can only introspect in-flight spans. This might not come in super handy, but it also might, given spans are very short-lived in practice.
  • We can refer to child spans by Span ID, instead of building an in-memory graph to traverse through. It helps obviate memory issues and trickiness we're observing with forked spans that outlive parents, and parents that might still want to hold onto references to child spans for traversal. [dnm] tracing: clean up use of auto collection across tasks #59391.
  • Another perk of having span-centric storage comes from util/tracing,sql: add builtin to set trace spans' verbosity #61353. At the time of writing, only root spans are accessible through the registry. So a built-in that's able to selectively toggle the verbosity of a span is of limited use; we can't isolate a random child span because it's only indirectly accessible (we'd need to traverse the tree of the root span).

Metadata

Metadata

Assignees

Labels

A-tracingRelating to tracing in CockroachDB.C-enhancementSolution expected to add code/behavior + preserve backward-compat (pg compat issues are exception)

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions