Stream throughput / latency bench
Node-side harnesses for measuring the streams backend and the browser mirror
under load. All of them speak the playground's capnweb WebSocket
(/api/streams), so they exercise the same WebSocket endpoint as the browser client.
Auth#
Local dev needs nothing. Deployed environments need an admin bearer:
export STREAMS_PLAYGROUND_TOKEN=$(doppler run --project streams-example-app --config <env> -- \
env WORKER_URL=https://<streams host> pnpm exec tsx e2e/auth.ts | tail -1)Tokens expire after 1h — a sudden WebSocket connection failed. on connect
usually means re-mint, not outage.
Scripts#
# single-append RTT distribution (commit + network floor)
pnpm exec tsx scripts/bench/bench-stream.mts rtt wss://<host>/api/streams /bench-rtt 100
# batched append throughput + correctness (acked vs server offset delta)
pnpm exec tsx scripts/bench/bench-stream.mts burst wss://<host>/api/streams /bench-b100 100000 100 64 20
# getEvents paging throughput
pnpm exec tsx scripts/bench/bench-stream.mts read wss://<host>/api/streams /bench-b100 0 100000 500
# paced sustained load with a 1s server-head timeline (for callback-lag analysis)
pnpm exec tsx scripts/bench/sustained-load.mts wss://<host>/api/streams /lat-test 5000 200 30
# end-to-end latency pings (pair with the browser sampler below)
pnpm exec tsx scripts/bench/ping-latency.mts wss://<host>/api/streams /lat-test 20 800Browser-side sampler#
For append→mirror latency and replay-progress curves, run this in the stream page's console (or via browser automation) and correlate timestamps with the ping/sustained-load output — both sides use the machine's wall clock:
window.__latSamples = [];
window.__lastOff = -1;
window.__latPoll = setInterval(() => {
const d = globalThis.__streamRuntimeDebug?.();
const v = d && Object.values(d)[0];
if (!v) return;
if (window.__lastOff !== v.lastDeliveredOffset) {
window.__lastOff = v.lastDeliveredOffset;
window.__latSamples.push({ offset: v.lastDeliveredOffset, t: Date.now() });
}
}, 5);__streamRuntimeDebug() also exposes deliveryArrivals / totalDeliveredEvents,
which is how you spot redundant redelivery after connection churn.
Known sharp edges these scripts measure (see PR notes)#
- Unpaced floods of >~1000 in-flight append calls make the Durable Object shed
load: in-flight calls fail
Network connection lost., a few commit without acks (retry without idempotency keys ⇒ duplicates). - A WebSocket connection stops carrying callback batches after ~1000 pushes (worker invocation subrequest budget), so big replays reconnect-thrash and redeliver.
- Browser OPFS ingest is far slower than the server's delivery rate and there is no backpressure; monster replays balloon memory.