Conversation
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
react-server-docs | e65b46a | Apr 27 2026, 09:20 PM |
⚡ Flight Protocol BenchmarkCommit: Serialization (
|
| Scenario | @lazarv/rsc | webpack | vs webpack |
|---|---|---|---|
| react: minimal element | 202.7K | 26.4K | 🟢 +668.1% |
| react: shallow wide (1000) | 2.1K | 341 | 🟢 +511.3% |
| react: deep nested (100) | 17.2K | 5.5K | 🟢 +212.3% |
| react: product list (50) | 6.2K | 1.8K | 🟢 +237.6% |
| react: large table (500x10) | 279 | 103 | 🟢 +172.1% |
| data: primitives | 177.0K | 36.1K | 🟢 +389.7% |
| data: large string (100KB) | 7.2K | 6.9K | 🟢 +3.6% |
| data: nested objects (20) | 58.6K | 25.3K | 🟢 +131.6% |
| data: large array (10K) | 118 | 111 | 🟢 +6.3% |
| data: Map & Set | 10.8K | 5.6K | 🟢 +92.8% |
| data: Date/BigInt/Symbol | 156.6K | 32.6K | 🟢 +380.8% |
| data: typed arrays | 33.1K | 14.2K | 🟢 +132.5% |
| data: mixed payload | 8.5K | 4.0K | 🟢 +112.7% |
Prerender (prerender)
| Scenario | @lazarv/rsc ops/s | mean |
|---|---|---|
| react: minimal element | 260.9K | 3.8 µs |
| react: shallow wide (1000) | 2.0K | 500.1 µs |
| react: deep nested (100) | 15.9K | 63.1 µs |
| react: product list (50) | 5.9K | 168.5 µs |
| react: large table (500x10) | 273 | 3.67 ms |
| data: primitives | 196.1K | 5.1 µs |
| data: large string (100KB) | 691 | 1.45 ms |
| data: nested objects (20) | 59.2K | 16.9 µs |
| data: large array (10K) | 118 | 8.47 ms |
| data: Map & Set | 11.2K | 89.1 µs |
| data: Date/BigInt/Symbol | 185.8K | 5.4 µs |
| data: typed arrays | 671 | 1.49 ms |
| data: mixed payload | 7.7K | 129.6 µs |
Deserialization (createFromReadableStream)
| Scenario | @lazarv/rsc | webpack | vs webpack |
|---|---|---|---|
| react: minimal element | 168.3K | 137.8K | 🟢 +22.1% |
| react: shallow wide (1000) | 24.1K | 2.0K | 🟢 +1098.2% |
| react: deep nested (100) | 99.1K | 19.2K | 🟢 +416.4% |
| react: product list (50) | 53.3K | 14.6K | 🟢 +265.5% |
| react: large table (500x10) | 4.2K | 2.2K | 🟢 +90.6% |
| data: primitives | 140.7K | 126.3K | 🟢 +11.4% |
| data: large string (100KB) | 35.1K | 35.8K | 🔴 -2.0% |
| data: nested objects (20) | 83.9K | 65.8K | 🟢 +27.5% |
| data: large array (10K) | 284 | 225 | 🟢 +26.3% |
| data: Map & Set | 16.7K | 14.3K | 🟢 +16.9% |
| data: Date/BigInt/Symbol | 135.8K | 109.3K | 🟢 +24.2% |
| data: typed arrays | 54.2K | 41.1K | 🟢 +32.0% |
| data: mixed payload | 25.9K | 13.2K | 🟢 +95.8% |
Roundtrip (serialize + deserialize)
| Scenario | @lazarv/rsc | webpack | vs webpack |
|---|---|---|---|
| react: minimal element | 102.8K | 20.6K | 🟢 +399.1% |
| react: shallow wide (1000) | 1.8K | 283 | 🟢 +522.1% |
| react: deep nested (100) | 13.7K | 3.9K | 🟢 +251.5% |
| react: product list (50) | 5.2K | 1.6K | 🟢 +220.2% |
| react: large table (500x10) | 264 | 91 | 🟢 +189.9% |
| data: primitives | 79.6K | 28.0K | 🟢 +184.3% |
| data: large string (100KB) | 6.4K | 6.6K | 🔴 -2.7% |
| data: nested objects (20) | 33.5K | 18.4K | 🟢 +81.7% |
| data: large array (10K) | 83 | 78 | 🟢 +6.6% |
| data: Map & Set | 6.2K | 3.9K | 🟢 +58.3% |
| data: Date/BigInt/Symbol | 71.5K | 22.2K | 🟢 +222.7% |
| data: typed arrays | 23.4K | 11.1K | 🟢 +111.5% |
| data: mixed payload | 5.4K | 3.0K | 🟢 +81.4% |
Legend & methodology
Indicators: 🟢 > 1% faster | 🔴 > 1% slower | ⚪ within noise margin
vs webpack: compares @lazarv/rsc against react-server-dom-webpack within the same run.
vs baseline: compares @lazarv/rsc against the previous main branch run.
Values shown are operations/second (higher is better). Each scenario runs for at least 100 iterations with warmup.
Benchmarks run on GitHub Actions runners (shared infrastructure) — expect ~5% variance between runs. Consistent directional changes across multiple scenarios are more meaningful than any single number.
⚡ Benchmark Results
Legend🟢 > 1% improvement | 🔴 > 1% regression | ⚪ within noise margin Benchmarks run on GitHub Actions runners (shared infrastructure) — expect ~5% variance between runs. Consistent directional changes across multiple routes are more meaningful than any single number. |
❌ 3 Tests Failed:
View the top 3 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
Hardens
react-server startfor production deployment behind a load balancer or a k8s/Docker orchestrator. The Node HTTP server now ships with sensible slow-loris and idle-connection timeouts, signals propagate correctly through the cluster master, workers drain in-flight requests onSIGTERMinstead of dropping them, and a crash-loop guard prevents fork-bombing the host on a deterministic boot failure. A new readiness endpoint reports worker liveness so external probes can route around a dead worker before the kernel reaps the socket.The headline addition is an adaptive admission controller built on
performance.eventLoopUtilization()with an AIMD update loop. Under load it expands the in-flight limit while ELU stays below target and contracts multiplicatively when the loop saturates, holding tail latency well below the unbounded baseline. Admission is FIFO across the wait queue so requests don't starve, and a fast-path release skips the EWMA bookkeeping on the hot path. Backpressure is opt-in by default and auto-enables only underreact-server startcluster mode — the feature is meaningful on Node, not on edge or serverless, and the import chain is gated accordingly. BothREACT_SERVER_BACKPRESSUREand abackpressure.enabledconfig key override the default in either direction.The static-asset handler was rewritten to use async
stat()with in-flight coalescing, a bounded pending map, and a bounded miss set, so a flood of unique paths can't blow the libuv thread pool or the heap. Hit/miss decisions stay synchronous in the steady state via a microtask-elision pattern that avoids aPromise.resolveround-trip on every request.While exercising the HTTPS path under HTTP/2, two pre-existing bugs surfaced and were fixed in the same branch since the HTTPS surface is on the critical path. Building a WHATWG
Requestfromreq.headersunder Node's HTTP/2 compat layer threwTypeError: Key Symbol(sensitiveHeaders) ... cannot be converted to a ByteStringbecause Node tags headers with an internal symbol and adds:method/:path/:authority/:schemepseudo-headers; the middleware now copies only string keys that don't begin with:. Separately, HTTP/1.1'skeepAliveTimeout/headersTimeout/requestTimeoutdon't apply to HTTP/2 sessions, so an HTTP/2 slow-loris would have hung the worker indefinitely; a session-levelsetTimeoutcloses the gap. A related discovery — Node's default 30sconnectionsCheckingIntervalsilently masks the configured timeouts — is fixed by passing a 5s interval tocreateServer()and exposing it as a config option.The runtime had no defenses against partial requests, no graceful shutdown story under k8s (the master is PID 1; the OS doesn't propagate signals to workers), no concurrency ceiling under burst load, and no way for an orchestrator to learn that a worker had died before the listener socket closed. Each gap was independently capable of producing dropped requests, runaway latency, or a thundering crash-loop in production. This branch closes all of them and lays the groundwork for the load-shedding behavior that the upcoming docs page describes.