Skip to content

[Flight] Fix stranded row content under Node stream backpressure#36516

Open
unstubbable wants to merge 1 commit into
facebook:mainfrom
unstubbable:fix-node-streams-back-pressure-bug
Open

[Flight] Fix stranded row content under Node stream backpressure#36516
unstubbable wants to merge 1 commit into
facebook:mainfrom
unstubbable:fix-node-streams-back-pressure-bug

Conversation

@unstubbable
Copy link
Copy Markdown
Collaborator

The Flight Server emits Text and TypedArray rows as two chunks: a header that gives the row's id, type, and content length, followed by the content itself. These two chunks were pushed into completedRegularChunks (and completedDebugChunks in DEV) as separate items, so when the destination signaled backpressure between them, the flush would write the header and then break out before reaching the content. The content chunk was left stranded at the head of the queue. Async work running while the destination was paused appended new rows to completedImportChunks / completedHintChunks, and the next drain flushed those queues first — splicing the newly-arrived bytes into the position the Flight Client expects to read as the original row's content. From there the Flight Client read rows from the wrong byte offsets and the model failed to deserialize.

This only surfaced on the Node stream path. createFakeWritableFromReadableStreamController, used by renderToReadableStream, always returns true from write(), so the flush loop never saw backpressure.

The fix pushes each pair as a single tuple [headerChunk, contentChunk] into completedRegularChunks and completedDebugChunks. The flush loops now write every part of a tuple before re-checking backpressure, so backpressure can still break between rows but never within one.

The Flight Server emits Text and TypedArray rows as two chunks: a header
that gives the row's id, type, and content length, followed by the
content itself. These two chunks were pushed into
`completedRegularChunks` (and `completedDebugChunks` in DEV) as separate
items, so when the destination signaled backpressure between them, the
flush would write the header and then break out before reaching the
content. The content chunk was left stranded at the head of the queue.
Async work running while the destination was paused appended new rows to
`completedImportChunks` / `completedHintChunks`, and the next drain
flushed those queues first — splicing the newly-arrived bytes into the
position the Flight Client expects to read as the original row's
content. From there the Flight Client read rows from the wrong byte
offsets and the model failed to deserialize.

This only surfaced on the Node stream path.
`createFakeWritableFromReadableStreamController`, used by
`renderToReadableStream`, always returns `true` from `write()`, so the
flush loop never saw backpressure.

The fix pushes each pair as a single tuple `[headerChunk, contentChunk]`
into `completedRegularChunks` and `completedDebugChunks`. The flush
loops now write every part of a tuple before re-checking backpressure,
so backpressure can still break between rows but never within one.
@meta-cla meta-cla Bot added the CLA Signed label May 21, 2026
@github-actions github-actions Bot added the React Core Team Opened by a member of the React Core Team label May 21, 2026
@react-sizebot
Copy link
Copy Markdown

Comparing: 008a6d4...f6d48c2

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.84 kB 6.84 kB = 1.88 kB 1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 614.17 kB 614.17 kB = 108.52 kB 108.52 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.84 kB 6.84 kB = 1.88 kB 1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 680.11 kB 680.11 kB = 119.48 kB 119.48 kB
facebook-www/ReactDOM-prod.classic.js = 700.53 kB 700.53 kB = 123.05 kB 123.05 kB
facebook-www/ReactDOM-prod.modern.js = 690.84 kB 690.84 kB = 121.44 kB 121.44 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable-semver/react-server/cjs/react-server-flight.development.js +0.90% 146.39 kB 147.71 kB +1.11% 26.23 kB 26.52 kB
oss-stable/react-server/cjs/react-server-flight.development.js +0.90% 146.39 kB 147.71 kB +1.11% 26.23 kB 26.52 kB
oss-experimental/react-server/cjs/react-server-flight.development.js +0.89% 148.48 kB 149.80 kB +1.03% 26.69 kB 26.97 kB
oss-stable-semver/react-server/cjs/react-server-flight.production.js +0.75% 66.24 kB 66.74 kB +0.92% 13.08 kB 13.20 kB
oss-stable/react-server/cjs/react-server-flight.production.js +0.75% 66.24 kB 66.74 kB +0.92% 13.08 kB 13.20 kB
oss-experimental/react-server/cjs/react-server-flight.production.js +0.73% 68.05 kB 68.54 kB +0.90% 13.49 kB 13.61 kB
facebook-www/ReactFlightServer-dev.classic.js +0.66% 199.45 kB 200.77 kB +0.63% 36.14 kB 36.37 kB
facebook-www/ReactFlightServer-dev.modern.js +0.66% 199.45 kB 200.77 kB +0.63% 36.14 kB 36.37 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-server.browser.development.js +0.64% 208.17 kB 209.49 kB +0.59% 37.72 kB 37.95 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-server.browser.development.js +0.64% 208.17 kB 209.49 kB +0.59% 37.72 kB 37.95 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-server.browser.development.js +0.63% 210.27 kB 211.59 kB +0.59% 38.14 kB 38.37 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-server.edge.development.js +0.62% 212.19 kB 213.51 kB +0.57% 38.32 kB 38.54 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-server.edge.development.js +0.62% 212.19 kB 213.51 kB +0.57% 38.32 kB 38.54 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-server.edge.development.js +0.62% 214.28 kB 215.60 kB +0.57% 38.73 kB 38.95 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.development.js +0.61% 215.78 kB 217.10 kB +0.59% 39.01 kB 39.24 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.development.js +0.61% 215.78 kB 217.10 kB +0.59% 39.01 kB 39.24 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js +0.61% 216.25 kB 217.57 kB +0.59% 39.12 kB 39.35 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js +0.61% 216.25 kB 217.57 kB +0.59% 39.12 kB 39.35 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.development.js +0.61% 217.88 kB 219.20 kB +0.57% 39.44 kB 39.66 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js +0.61% 218.36 kB 219.68 kB +0.57% 39.55 kB 39.78 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js +0.60% 219.91 kB 221.23 kB +0.56% 39.62 kB 39.84 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js +0.60% 219.91 kB 221.23 kB +0.56% 39.62 kB 39.84 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.development.js +0.60% 219.91 kB 221.23 kB +0.57% 39.62 kB 39.85 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.development.js +0.60% 219.91 kB 221.23 kB +0.57% 39.62 kB 39.85 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js +0.60% 222.00 kB 223.32 kB +0.56% 40.04 kB 40.26 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.development.js +0.60% 222.00 kB 223.32 kB +0.56% 40.04 kB 40.26 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +0.56% 237.28 kB 238.60 kB +0.53% 43.17 kB 43.40 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +0.56% 237.28 kB 238.60 kB +0.53% 43.17 kB 43.40 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +0.55% 239.37 kB 240.69 kB +0.50% 43.61 kB 43.83 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-server.node.development.js +0.54% 244.05 kB 245.37 kB +0.56% 43.82 kB 44.07 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-server.node.development.js +0.54% 244.05 kB 245.37 kB +0.56% 43.82 kB 44.07 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-server.node.development.js +0.54% 246.14 kB 247.46 kB +0.54% 44.25 kB 44.49 kB
oss-stable-semver/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.development.js +0.53% 250.57 kB 251.89 kB +0.52% 44.90 kB 45.13 kB
oss-stable/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.development.js +0.53% 250.57 kB 251.89 kB +0.52% 44.90 kB 45.13 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js +0.53% 251.77 kB 253.09 kB +0.51% 45.21 kB 45.44 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js +0.53% 251.77 kB 253.09 kB +0.51% 45.21 kB 45.44 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.development.js +0.53% 251.78 kB 253.10 kB +0.51% 45.22 kB 45.45 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.development.js +0.53% 251.78 kB 253.10 kB +0.51% 45.22 kB 45.45 kB
oss-experimental/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.development.js +0.52% 252.66 kB 253.98 kB +0.49% 45.33 kB 45.55 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js +0.52% 253.86 kB 255.18 kB +0.49% 45.64 kB 45.86 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.development.js +0.52% 253.87 kB 255.19 kB +0.49% 45.65 kB 45.87 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-server.browser.production.js +0.49% 108.30 kB 108.83 kB +0.53% 21.10 kB 21.21 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-server.browser.production.js +0.49% 108.30 kB 108.83 kB +0.53% 21.10 kB 21.21 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-server.browser.production.js +0.48% 110.09 kB 110.63 kB +0.49% 21.46 kB 21.57 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-server.edge.production.js +0.47% 112.48 kB 113.02 kB +0.52% 22.80 kB 22.92 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-server.edge.production.js +0.47% 112.48 kB 113.02 kB +0.52% 22.80 kB 22.92 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js +0.47% 113.81 kB 114.35 kB +0.48% 22.02 kB 22.13 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js +0.47% 113.81 kB 114.35 kB +0.48% 22.02 kB 22.13 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js +0.47% 114.17 kB 114.70 kB +0.49% 22.11 kB 22.22 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js +0.47% 114.17 kB 114.70 kB +0.49% 22.11 kB 22.22 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-server.edge.production.js +0.47% 114.28 kB 114.81 kB +0.50% 23.19 kB 23.30 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js +0.46% 115.61 kB 116.14 kB +0.52% 22.38 kB 22.49 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js +0.46% 115.96 kB 116.49 kB +0.49% 22.48 kB 22.59 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js +0.45% 119.42 kB 119.95 kB +0.48% 23.97 kB 24.09 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js +0.45% 119.42 kB 119.95 kB +0.48% 23.97 kB 24.09 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js +0.45% 119.42 kB 119.95 kB +0.47% 23.96 kB 24.07 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js +0.45% 119.42 kB 119.95 kB +0.47% 23.96 kB 24.07 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js +0.44% 121.21 kB 121.74 kB +0.47% 24.34 kB 24.45 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js +0.44% 121.21 kB 121.74 kB +0.47% 24.33 kB 24.44 kB
facebook-www/ReactFlightServer-prod.classic.js +0.41% 103.32 kB 103.74 kB +0.55% 20.95 kB 21.06 kB
facebook-www/ReactFlightServer-prod.modern.js +0.41% 103.32 kB 103.74 kB +0.55% 20.95 kB 21.06 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.js +0.36% 116.50 kB 116.93 kB +0.45% 23.58 kB 23.68 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.js +0.36% 116.50 kB 116.93 kB +0.45% 23.58 kB 23.68 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.js +0.36% 118.30 kB 118.72 kB +0.46% 23.95 kB 24.05 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-server.node.production.js +0.35% 120.57 kB 120.99 kB +0.41% 24.16 kB 24.26 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-server.node.production.js +0.35% 120.57 kB 120.99 kB +0.41% 24.16 kB 24.26 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-server.node.production.js +0.35% 122.36 kB 122.79 kB +0.46% 24.52 kB 24.63 kB
oss-stable-semver/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js +0.33% 126.44 kB 126.86 kB +0.47% 25.09 kB 25.21 kB
oss-stable/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js +0.33% 126.44 kB 126.86 kB +0.47% 25.09 kB 25.21 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js +0.33% 127.48 kB 127.91 kB +0.43% 25.33 kB 25.44 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js +0.33% 127.48 kB 127.91 kB +0.43% 25.33 kB 25.44 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js +0.33% 127.50 kB 127.92 kB +0.42% 25.32 kB 25.43 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js +0.33% 127.50 kB 127.92 kB +0.42% 25.32 kB 25.43 kB
oss-experimental/react-server-dom-unbundled/cjs/react-server-dom-unbundled-server.node.production.js +0.33% 128.23 kB 128.65 kB +0.42% 25.47 kB 25.58 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js +0.33% 129.28 kB 129.70 kB +0.42% 25.70 kB 25.81 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js +0.33% 129.29 kB 129.71 kB +0.41% 25.69 kB 25.80 kB

Generated by 🚫 dangerJS against f6d48c2

@unstubbable unstubbable marked this pull request as ready for review May 21, 2026 17:27
@unstubbable unstubbable requested a review from gnoff May 21, 2026 17:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed React Core Team Opened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants