Skip to content

[Flight] Make debug info and console log resolve in predictable order #33665

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

sebmarkbage
Copy link
Collaborator

Stacked on #33664.

This resolves an outstanding issue where it was possible for debug info and console logs to become out of order if they up blocked. E.g. by a future reference or a client reference that hasn't loaded yet. Such as if you console.log a client reference followed by one that doesn't. This encodes the order similar to how the stream chunks work.

This also blocks the main chunk from resolving until the last debug info has fully loaded, including future references and client references. This also ensures that we could send some of that data in a different stream, since then it can come out of order.

@sebmarkbage sebmarkbage requested a review from unstubbable June 29, 2025 03:26
@github-actions github-actions bot added the React Core Team Opened by a member of the React Core Team label Jun 29, 2025
@react-sizebot
Copy link

react-sizebot commented Jun 29, 2025

Comparing: da7487b...d685720

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.68 kB 6.68 kB = 1.83 kB 1.83 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 530.70 kB 530.70 kB = 93.70 kB 93.70 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.69 kB 6.69 kB = 1.83 kB 1.83 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 655.25 kB 655.25 kB = 115.40 kB 115.40 kB
facebook-www/ReactDOM-prod.classic.js = 675.13 kB 675.13 kB = 118.75 kB 118.75 kB
facebook-www/ReactDOM-prod.modern.js = 665.56 kB 665.56 kB = 117.12 kB 117.12 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-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +1.79% 116.33 kB 118.42 kB +1.60% 21.52 kB 21.87 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +1.79% 116.38 kB 118.47 kB +1.61% 21.55 kB 21.89 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js +1.77% 117.47 kB 119.56 kB +1.69% 21.55 kB 21.91 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js +1.77% 117.52 kB 119.61 kB +1.68% 21.58 kB 21.94 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js +1.76% 116.32 kB 118.37 kB +1.69% 21.74 kB 22.10 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js +1.76% 116.32 kB 118.37 kB +1.69% 21.74 kB 22.10 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js +1.76% 118.48 kB 120.56 kB +1.60% 21.92 kB 22.27 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js +1.76% 118.53 kB 120.61 kB +1.60% 21.94 kB 22.29 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js +1.75% 119.07 kB 121.16 kB +1.57% 22.05 kB 22.40 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js +1.75% 119.12 kB 121.21 kB +1.57% 22.08 kB 22.43 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +1.74% 119.62 kB 121.70 kB +1.65% 22.01 kB 22.37 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +1.74% 119.62 kB 121.70 kB +1.65% 22.01 kB 22.37 kB
oss-stable-semver/react-client/cjs/react-client-flight.development.js +1.74% 112.40 kB 114.35 kB +1.86% 20.41 kB 20.79 kB
oss-stable/react-client/cjs/react-client-flight.development.js +1.74% 112.42 kB 114.38 kB +1.86% 20.43 kB 20.81 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js +1.72% 119.32 kB 121.38 kB +1.64% 22.21 kB 22.57 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js +1.72% 119.32 kB 121.38 kB +1.64% 22.21 kB 22.57 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js +1.72% 119.45 kB 121.50 kB +1.65% 22.24 kB 22.61 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js +1.72% 119.45 kB 121.50 kB +1.65% 22.24 kB 22.61 kB
oss-stable-semver/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js +1.71% 121.74 kB 123.82 kB +1.69% 22.47 kB 22.85 kB
oss-stable/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js +1.71% 121.74 kB 123.82 kB +1.69% 22.47 kB 22.85 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.development.js +1.69% 123.39 kB 125.48 kB +1.67% 22.75 kB 23.13 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.development.js +1.69% 123.39 kB 125.48 kB +1.67% 22.75 kB 23.13 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js +1.67% 124.60 kB 126.68 kB +1.66% 22.96 kB 23.34 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js +1.67% 124.60 kB 126.68 kB +1.66% 22.96 kB 23.34 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js +1.67% 124.72 kB 126.81 kB +1.66% 23.00 kB 23.38 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js +1.67% 124.72 kB 126.81 kB +1.66% 23.00 kB 23.38 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +1.62% 161.74 kB 164.36 kB +0.98% 37.00 kB 37.36 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +1.62% 161.77 kB 164.38 kB +0.98% 37.02 kB 37.38 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +1.32% 198.23 kB 200.85 kB +0.83% 43.83 kB 44.19 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.edge.development.js +1.24% 158.75 kB 160.73 kB +1.09% 28.59 kB 28.90 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +1.24% 158.83 kB 160.79 kB +1.22% 28.27 kB 28.61 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.browser.development.js +1.23% 159.97 kB 161.93 kB +1.27% 28.29 kB 28.65 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.edge.development.js +1.22% 161.76 kB 163.73 kB +1.06% 29.07 kB 29.38 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.browser.development.js +1.22% 160.97 kB 162.94 kB +1.15% 28.68 kB 29.01 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js +1.22% 161.88 kB 163.86 kB +1.05% 29.10 kB 29.41 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.development.js +1.22% 161.57 kB 163.53 kB +1.13% 28.82 kB 29.15 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +1.21% 162.05 kB 164.01 kB +1.15% 28.83 kB 29.16 kB
oss-experimental/react-server-dom-parcel/cjs/react-server-dom-parcel-client.node.development.js +1.20% 164.17 kB 166.13 kB +1.15% 29.26 kB 29.59 kB
oss-experimental/react-client/cjs/react-client-flight.development.js +1.19% 154.86 kB 156.70 kB +1.31% 27.22 kB 27.58 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.unbundled.development.js +1.18% 165.82 kB 167.79 kB +1.13% 29.54 kB 29.87 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-client.node.development.js +1.18% 167.03 kB 168.99 kB +1.12% 29.76 kB 30.09 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-client.node.development.js +1.17% 167.16 kB 169.12 kB +1.11% 29.79 kB 30.13 kB
oss-experimental/react-markup/cjs/react-markup.react-server.development.js +0.53% 646.95 kB 650.40 kB +0.43% 114.24 kB 114.73 kB

Generated by 🚫 dangerJS against d685720

Comment on lines +3236 to +3437
type ConsoleEntry = [
string,
ReactStackTrace,
null | ReactComponentInfo,
string,
mixed,
];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
type ConsoleEntry = [
string,
ReactStackTrace,
null | ReactComponentInfo,
string,
mixed,
];
type ConsoleEntry = [
/* eslint-disable no-undef -- eslint does not understand Flow tuple syntax. */
methodName: string,
stackTrace: ReactStackTrace,
owner: null | ReactComponentInfo,
env: string,
args: mixed,
/* eslint-enable no-undef */
];

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does this error? I'm not seeing any lint errors.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In your version it does not error. I wanted to suggest adding the tuple labels. And then it would error unless we disable eslint. Devon did the same here:

export type ImportMetadata = [
// eslint does not understand Flow tuple syntax.
/* eslint-disable */
id: string,
name: string,
bundles: Array<string>,
importMap?: {[string]: string},
/* eslint-enable */
];

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given how much trouble we've had with parsers I'm not sure I trust that this won't break linting elsewhere.

@sebmarkbage sebmarkbage force-pushed the debugstream branch 2 times, most recently from 0236f23 to 636f359 Compare June 29, 2025 14:57
stream,
controller,
);
blockedDebugInfo.then(unblock, unblock);
Copy link
Collaborator Author

@sebmarkbage sebmarkbage Jun 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is safe because the chunk needs to enter a non-pending state before the next chunk is resolved. Otherwise the next chunk doesn't know it's a stream entry.

@eps1lon
Copy link
Collaborator

eps1lon commented Jul 1, 2025

Do we block forever or do we give up after a timeout and log a dumbed down value after some time?

We could log a placeholder object if it takes to long to resolve the value and set a dedicated property later. We'd end up logging something slightly different but when you have a debugger attached, you could inspect the resolved value later for debuggers where logged values are live (e.g. Chrome DevTools)

@sebmarkbage
Copy link
Collaborator Author

Currently the only thing that can block it is a client reference and it's kind of unusual that you would be able to render anything at all without it. Everything else comes before in the stream anyway.

However, the main reason to do this is to allow the separate debug stream. I think for that one, it's up to the framework to ensure that it's live connected and if it closes then we'd fall back to skipping debug info.

sebmarkbage added a commit that referenced this pull request Jul 10, 2025
This lets us pass a writable on the server side and readable on the
client side to send debug info through a separate channel so that it
doesn't interfere with the main payload as much. The main payload refers
to chunks defined in the debug info which means it's still blocked on it
though. This ensures that the debug data has loaded by the time the
value is rendered so that the next step can forward the data.

This will be a bit fragile to race conditions until #33665 lands.
Another follow up needed is the ability to skip the debug channel on the
receiving side. Right now it'll block forever if you don't provide one
since we're blocking on the debug data.
sebmarkbage added a commit that referenced this pull request Jul 15, 2025
React Elements reference debug data (their stack and owner) in the debug
channel. If the debug channel isn't wired up this can block the client
from resolving.

We can infer that if there's no debug channel wired up and the reference
wasn't emitted before the element, then it's probably because it's in
the debug channel. So we can skip it.

This should also apply to debug chunks but they're not yet blocking
until #33665 lands.
@sebmarkbage
Copy link
Collaborator Author

sebmarkbage commented Jul 17, 2025

There's two issues with this approach that I don't like.

  1. It's still eagerly initialized which means that we have to parse all the debug info before the thing that references it is used. Ideally we'd adjust the debug info protocol so that you have to initialize the Promise before accessing the debug info. This would also ensure that it's more lazy about lazy loading deeper objects instead of eagerly as they're parsed in the stream.

  2. Because the dependencies between debug info chunks are modeled as callbacks instead of the InitializingReference added in [Flight] Resolve Deep Cycles #33664, they cannot resolve cycles. I added a failing test to show this.

Neither of these are an issue for the console part of this since it's an eager side-effect regardless to preserve order and there's nothing else in the stream that depends on a console entry, where as you can depend on a chunk which is blocked by debug info.

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.

5 participants