Skip to content

perf: append the undici traceparent without addHeader() - #37

Open
bengl wants to merge 1 commit into
nodejs:mainfrom
bengl:bengl/undici-header-push
Open

bengl wants to merge 1 commit into
nodejs:mainfrom
bengl:bengl/undici-header-push

Conversation

@bengl

@bengl bengl commented Nov 3, 2026

Copy link
Copy Markdown
Member

The undici instrumentation adds traceparent to every outgoing undici and fetch request, and tracestate and baggage when the span has them. It added each one with request.addHeader(), which validates the pair before appending it to undici's flat array of header names and values. traceparent now goes onto that array directly.

Why only traceparent. microtel builds the traceparent value from IDs and flags it generated or already validated, so addHeader() has nothing to catch there, and every request carries one. tracestate and baggage carry values that came from elsewhere, so they keep going through addHeader(). A test checks which names reach it.

Why it's safe. For a lowercase name that isn't one of undici's special headers, such as host or content-length, addHeader() validates the value and pushes the pair as given. undici's HTTP/1 writer and HTTP/2 header builder both read the array in pairs. When the headers aren't an even-length array, the append falls back to addHeader(), so an undici that stores headers differently can't break requests.

Performance. Per appended header, on Node 26.12.1, over 7 interleaved runs:

Median Samples (ns)
addHeader() 76.7 ns 75.2, 75.8, 76.5, 76.7, 77.1, 77.4, 77.6
Direct push 16.5 ns 16.0, 16.1, 16.2, 16.5, 16.6, 17.1, 18.3

That's about 60 ns per request, around 0.1% of a traced fetch, which is below this machine's noise in the fetch benchmark.

Tests.

  • A fetch carries the full trace context, with each header appended rather than overriding a caller's.
  • A fixture spying on addHeader() sees tracestate and baggage but not traceparent.
  • Fixtures whose request headers are an object or an odd-length array still send a valid traceparent, through addHeader().

Previously, the undici instrumentation added traceparent, tracestate,
and baggage to each outgoing request with request.addHeader(), which
validates each pair before appending it. Now an appended traceparent
goes straight onto undici's flat array of header names and values.
microtel makes that value, so the validation had nothing to catch.
tracestate and baggage carry values from elsewhere and keep going
through addHeader(), and so does any header when the array is not in
that shape.

Appending a header drops from about 77 ns to about 16.

Signed-off-by: Bryan English <bryan@bryanenglish.com>
@bengl
bengl marked this pull request as ready for review November 3, 2026 19:43
@bengl
bengl requested a review from a team November 3, 2026 19:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants