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
perf(propagation): tighten tracestate, baggage, and tag inject paths (#8234)
* perf(propagation): tighten baggage and tag inject paths
W3C baggage extract and `_dd.p.*` tag inject both run on every
traced HTTP request. Several sub-allocations are dropped:
`text_map.js` swaps three single-char-class `replaceAll(/[\xNN]/g, ...)`
regex literals for `replaceAll('=', '~')` / `replaceAll('~', '=')`,
which skip the regex match path. `_injectTags` and
`_injectTraceparent` walk `trace.tags` via `Object.keys(...)`
instead of banned `for-in`; the trace-tags object's prototype
chain isn't ours to trust, and `for-in` enumerates inherited
keys. `_injectBaggageItems` swaps `Object.entries` for
`Object.keys` + indexed read, dropping the per-baggage-item
`[k, v]` tuple. `_extractBaggageItems` caches the `baggageTagKeys`
`Set` on the propagator (rebuilt only when the config array
reference changes, e.g. remote-config rotation) and gates
`decodeURIComponent` behind `value.includes('%')` — a
microbenchmark pins the gated path at 13.4x faster than running
`decodeURIComponent` on plain ASCII baggage and only 2% slower
than the raw call on percent-encoded values.
`tracestate.js#forVendor` reuses the `state.toString()` value
computed one line above instead of recomputing it.
The original draft also rewrote `tracestate#fromString` to drop
an `Array#unshift` quadratic; #8256 landed a linear parser first
that supersedes those hunks, so they're dropped.
* perf(dsm): trim per-checkpoint and per-message allocations
DSM observes every Kafka, SQS, SNS, Kinesis, Pub/Sub, and AMQP
message when enabled, so the per-checkpoint hot path compounds.
Several allocations are removed without changing the wire format:
1. `getSizeOrZero` stopped allocating a fresh Buffer copy of every
string just to read its UTF-8 byte length. `Buffer.byteLength`
returns the same value with no allocation. `getHeadersSize`'s
`Object.entries(...).reduce(...)` becomes a `for (const key of
Object.keys(headers))` loop, dropping the per-header `[k, v]`
tuple and the reducer closure.
2. `pathway.js#shaHash` extracted the first 8 bytes of SHA-256 by
round-tripping through a 64-char hex string + a 16-char slice +
a hex-decoded Buffer. `digest().subarray(0, 8)` produces the same
bytes directly. `computeHash` now also caches
`hashableEdgeTags.join('')` and `propagationHashBigInt.toString(16)`
once per call (each was computed twice), gates the
`manual_checkpoint:true` filter on `includes(...)` so the common
path skips the alloc, and reuses a module-scope 20-byte scratch
buffer to assemble `encodePathwayContext` with a single
`Buffer.from(subarray)` copy-out instead of seven nested allocs.
3. `setCheckpoint` precomputes `PATHWAY_HEADER_BYTES` from the static
header overhead instead of allocating a temp object, encoding
it, and JSON-stringifying just to read its length. It now reads
the direction from `edgeTags[0]` directly: every in-tree caller
places it there, the `DataStreamsCheckpointer` shape is updated
to match, and the test fixture pinning that arg order is updated
in the same commit.
Drive-by fix:
* `recordCheckpoint` reuses the `BigInt` already computed by the
`StatsPoint` returned from `forCheckpoint(...)` instead of running
`readBigUInt64LE` a second time. `setCheckpoint` returns
`undefined` (rather than `null`) on the disabled fast path so
the function shape matches the rest of the file.
* `processor.js` drops the `DsmPathwayCodec` import that the
inlined byte-count made unreachable; `pathway.js` exports
`CONTEXT_PROPAGATION_KEY_BASE64` so the constant calculation is
anchored to the actual header key.
* `encoding.js` adds an `encodeVarintInto(target, offset, value)`
helper so the pathway encoder can write directly into the scratch
buffer instead of allocating a per-varint `Uint8Array` and
copying.
0 commit comments