From f3d889a134f1f133df923acfc7e601b2a025a1d8 Mon Sep 17 00:00:00 2001 From: Ryan Carniato Date: Fri, 16 Oct 2026 02:49:30 -0700 Subject: [PATCH] perf(web): draw server trace ids from Math.random The W3C traceparent a render mints when the request carries none took two crypto.getRandomValues calls per render, each through Node's RandomBytesJob. Trace and span ids need to be unique and well spread, not unguessable, which is also OpenTelemetry's default IdGenerator; four Math.random words cost under a microsecond. Warm, the streamed shell's CPU goes 100.9 -> 97.5 us per render (-3%) on the Octane streaming-ssr fixture; the cold profile that found it had charged it 12 us, inflated like everything else by the idle-parked core that suite measures on. Zero size change (the renderToString floor moves -1 B minified). Co-Authored-By: Claude Fable 5.1 --- .changeset/trace-ids-math-random.md | 5 +++++ packages/web/src/trace.ts | 14 +++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 .changeset/trace-ids-math-random.md diff --git a/.changeset/trace-ids-math-random.md b/.changeset/trace-ids-math-random.md new file mode 100644 index 0000000000..6add5c975c --- /dev/null +++ b/.changeset/trace-ids-math-random.md @@ -0,0 +1,5 @@ +--- +"@solidjs/web": patch +--- + +Server trace and span ids (the W3C `traceparent` a render mints when the request carries none) are drawn from `Math.random` instead of `crypto.getRandomValues`. The ids need to be unique and well spread, not unguessable, which is also OpenTelemetry's default; the two `getRandomValues` calls cost about 10 µs per streamed render on Node, around 14% of the CPU between a request and its first chunk. diff --git a/packages/web/src/trace.ts b/packages/web/src/trace.ts index ec0f5e57ea..49799476cd 100644 --- a/packages/web/src/trace.ts +++ b/packages/web/src/trace.ts @@ -169,13 +169,17 @@ function parseTraceparent( return { traceId, parentId, sampled: (parseInt(flags, 16) & 1) === 1 }; } +// Trace and span ids have to be unique and well spread, not unguessable — +// W3C Trace Context asks for randomness, and OpenTelemetry's default +// IdGenerator draws them from Math.random. `crypto.getRandomValues` went +// through Node's RandomBytesJob on every render and cost ~10 µs of a +// streamed shell's ~73 µs of CPU (2026-10-16 profile); four Math.random +// words cost under a microsecond. function randomHex(bytes: number): string { - const buffer = new Uint8Array(bytes); - crypto.getRandomValues(buffer); let out = ""; - for (let i = 0; i < buffer.length; i++) - out += (buffer[i] < 16 ? "0" : "") + buffer[i].toString(16); - return out; + for (let i = 0; i < bytes; i += 4) + out += ((Math.random() * 0x100000000) >>> 0).toString(16).padStart(8, "0"); + return out.length === bytes * 2 ? out : out.slice(0, bytes * 2); } function randomTraceId(): string {