You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(profiling): resolve web tags for descendants cached before promotion
The shared web-tags cache records "no web-server ancestor" for a span and
never revisits it, but that answer expires: plugins set `span.type` after
creating the span — TracingPlugin.startSpan activates it before addRequestTags
runs — so a child created in that window walks past an ancestor that is about
to become a web-server span, and caches a miss for a chain that is about to
have one.
Promotion can't find those descendants, since the walk only goes upwards. So a
promotion now bumps a generation counter, and an empty answer older than the
counter is walked again on the next lookup. Resolved answers are untouched (the
cached bag is the ancestor's live tag object), and a span with no parent is
stamped as permanently empty, since only its own promotion could change it and
onTagsUpdate already handles that. When a re-walk turns an empty answer into a
real one, the cache publishes resolvedCh for that span, the same announcement a
promoted span gets, so a consumer doesn't have to care which way the ancestry
appeared.
The counter lives on the trace, not in the module. A promotion can only
invalidate empty answers within its own trace, because the walk follows
`_parentId` through `_trace.started` and never leaves it — while a
process-global counter would have every HTTP request's promotion invalidate
empty answers in unrelated traces, making a long-lived non-web span re-walk its
chain once per request served elsewhere, from the storage-enter path.
Invalidation alone fixes nothing, because both consumers only ask the cache
while building their per-span state, and the spans this affects already have
theirs built. So each now asks again for state built from an empty answer:
- the OTEP-4947 writer re-checks on re-entry when a record was built with no
web-server ancestor, and attaches the endpoint or enlists the record for the
request's endpoint announcement. Guarded so a re-entrant announcement from
inside the lookup can't append the endpoint twice.
- the wall profiler re-checks in #getProfilingContext when the snapshot it
holds has no webTags, since #spanTagsUpdated only fires for a span promoted
itself, never for descendants that walked past it beforehand.
Both re-checks cost two property reads plus, at most, the cache's own
generation compare — a walk only happens when a promotion in that trace has
actually invalidated something.
Two test-harness inaccuracies are fixed along the way, both of which had been
hiding behaviour rather than testing it: web-tags-cache.spec.js's makeSpan
returned a fresh object from context() per call, so spying on it counted calls
on a throwaway and the "walks the parent chain once" assertion held vacuously;
and wall.spec.js's makeChildSpan gave the child its own _trace object, so
parent and child were in different traces, which no real trace chunk is.
Reported by codex on #9210 and #9805.
0 commit comments