-
Notifications
You must be signed in to change notification settings - Fork 50.2k
[Flight] Resolve deduped references when chunks are blocked on each other #32316
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
[Flight] Resolve deduped references when chunks are blocked on each other #32316
Conversation
414c443 to
ecb5a8a
Compare
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js
Show resolved
Hide resolved
| reason: null | Array<(mixed) => mixed>, | ||
| intermediaryValue?: mixed, | ||
| _response: Response, | ||
| _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not "Profiling-only" anymore. I'm not sure this is actually reliable enough. It's more of a hack for profiling which is optional if it works or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can keep _children profiling-only. It was not necessary for this fix to remove the two guards around initializingChunk._children. I reverted that.
| function createFromJSONCallback(response: Response) { | ||
| // $FlowFixMe[missing-this-annot] | ||
| return function (key: string, value: JSONValue) { | ||
| isParsingRootModel = key === ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't assume that an empty string key is the root since it may also be on nested objects. What's the implication of that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, we were already using the check here:
react/packages/react-client/src/ReactFlightClient.js
Lines 983 to 987 in 192555b
| // If this is the root object for a model reference, where `handler.value` | |
| // is a stale `null`, the resolved value can be used directly. | |
| if (key === '' && handler.value === null) { | |
| handler.value = mappedValue; | |
| } |
But I guess you're right, it could also be an explicit empty key in some object.
Stacked on facebook#33666. If we ever get a future reference to a cycle and that reference gets eagerly parsed before the target has loaded then we can end up with a cycle that never gets resolved. That's because our cycle resolution only works if the cyclic future reference is created synchronously within the parsing path of the child. I haven't been able to construct a normal scenario where this would break. So this doesn't fail any tests. However, I can construct it with debug info since those are eagerly evaluated. It's also a prerequisite if the debug data can come out of order, like if it's on a different stream. The fix here is to make all the internal dependencies in the "listener" list into introspectable objects instead of closures. That way we can traverse the list of dependencies of a blocked reference to see if it ends up in a cycle and therefore skip the reference. It would be nice to address this once and for all to be more resilient to server changes, but I'm not sure if it's worth this complexity and the extra CPU cost of tracing the dependencies. Especially if it's just for debug data. closes facebook#32316 fixes vercel/next.js#72104 --------- Co-authored-by: Hendrik Liebau <[email protected]>
This PR includes a reduced test case of vercel/next.js#75726:
This is serialized into the following RSC payload which the Flight Client was not able to resolve, because two chunks are blocked on each other:
We can fix this by storing intermediary values (only elements for now) on the initializing chunk. When the
fulfilllistener inwaitForReferenceis called with a lazy element that has a blocked chunk, which is not the current chunk, we can peek into the intermediary value of the referenced chunk to resolve a reference for a different chunk that's still blocked.fixes vercel/next.js#72104