AsyncLocalStorage Overhead: Measure Before Wrapping Every Request Path

AsyncLocalStorage Overhead: Measure Before Wrapping Every Request Path
14 views
AsyncLocalStorage (ALS) is the right default for request IDs and tenant context — until a 50k RPS gateway pays for a store enter/exit on every hop through middleware, ORM, and fetch. Measure the overhead on your Node version, then choose ALS, explicit params, or a hybrid.
⚡ TL;DR: Benchmark ALS vs explicit ctx args on hot handlers; keep one ALS store for correlation; don’t nest stores per library; disable ALS in ultra-hot pure functions. Pair with Node Diagnostic Channels and Node.js Event Loop Lag p99.

AsyncLocalStorage Overhead: production guidance

typescript
// scripts/bench-als.ts
import { AsyncLocalStorage } from "node:async_hooks";
import { performance } from "node:perf_hooks";

const als = new AsyncLocalStorage<{ reqId: string }>();

function bare(n: number) {
 let x = 0;
 for (let i = 0; i < n; i++) x += i;
 return x;
}

function withAls(n: number) {
 return als.run({ reqId: "b" }, () => {
 let x = 0;
 for (let i = 0; i < n; i++) {
 als.getStore(); // typical middleware read
 x += i;
 }
 return x;
 });
}

function bench(fn: (n: number) => number) {
 const n = 5_000_000;
 const t0 = performance.now();
 fn(n);
 return performance.now() - t0;
}

console.log({ bare: bench(bare), als: bench(withAls) });
Run on the same Node major as production. If ALS adds single-digit percent on a CPU-bound microbench, real I/O-bound APIs often absorb it — but gateways that already fight for microseconds should stay skeptical.

Lean context pattern

typescript
// lib/context.ts
import { AsyncLocalStorage } from "node:async_hooks";

export type ReqCtx = { reqId: string; tenantId: string };
export const reqAls = new AsyncLocalStorage<ReqCtx>();

export function getCtx(): ReqCtx {
 const s = reqAls.getStore();
 if (!s) throw new Error("missing_request_context");
 return s;
}

// ✅ Hot pure math / codec path — pass args, skip ALS
export function encodeInvoice(totalCents: number, tenantId: string) {
 return Buffer.from(`${tenantId}:${totalCents}`);
}
typescript
// server middleware — one enter per request
app.use((req, res, next) => {
 reqAls.run(
 { reqId: req.headers["x-request-id"] as string ?? crypto.randomUUID(),
 tenantId: String(req.headers["x-tenant-id"] ?? "") },
 () => next(),
 );
});

Hybrid for high-QPS gateways

Layer Carrier
Edge auth / HTTP middleware ALS OK
ORM / HTTP client wrappers read ALS once, pass explicit
Pure compute / codecs explicit args only
Worker threads structured clone of ctx snapshot
Avoid multiple ALS instances (OpenTelemetry + app + logger each nesting). Prefer one store and diagnostic channels for library metrics — see also undici pools.

Closing checklist

✅ Dos – ✅ Bench ALS on your Node version and hardware – ✅ Single store for request correlation – ✅ Explicit args on ultra-hot pure functions – ✅ Snapshot ctx when crossing into worker threads – ✅ Keep getStore() off the per-byte codec path ❌ Don’ts – ❌ Don’t nest a new ALS per dependency – ❌ Don’t ban ALS globally without numbers – ❌ Don’t forget lost context across poorly promisified callbacks – ❌ Don’t store huge objects in ALS (retainers → heap pressure)

Related reading

Last updated on September 11, 2026 Related: Lambda Cold Starts on Node 20: Measure, Cut, and Keep Cutting; Continuous Path Verification: Minute Canaries for Auth, Pay, and Search; Python GIL Contention: Logging Handlers That Stall Hot Request Paths.

Watch: 100 Days of AI on YouTube

Short videos from CheatCoders, one AI engineering topic per day.

Open the playlist on YouTube · CheatCoders on YouTube

Deep-dive PDF

Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.

Already subscribed? or open the subscribe page.


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a comment

No account needed. Name and email are optional.