Skip to content
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] Use lazy ID if referenced model chunk was not emitted yet #28620

Closed
wants to merge 4 commits into from

Conversation

unstubbable
Copy link
Contributor

@unstubbable unstubbable commented Mar 23, 2024

Summary

With this change, we keep track of emitted model chunk IDs. This allows us to properly decide whether to use a value ID or a lazy ID for a referenced model.

fixes #28595

How did you test this change?

@unstubbable unstubbable changed the title Choose lazy or value ID based on whether model chunk was emitted [Flight] Choose lazy or value ID based on whether model chunk was emitted Mar 23, 2024
@react-sizebot
Copy link

react-sizebot commented Mar 23, 2024

Comparing: f09e159...3b41b85

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.min.js = 176.93 kB 176.93 kB = 54.95 kB 54.95 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 173.36 kB 173.36 kB = 54.08 kB 54.08 kB
facebook-www/ReactDOM-prod.classic.js = 594.64 kB 594.64 kB = 104.47 kB 104.47 kB
facebook-www/ReactDOM-prod.modern.js = 577.90 kB 577.90 kB = 101.50 kB 101.50 kB
test_utils/ReactAllWarnings.js Deleted 65.49 kB 0.00 kB Deleted 16.16 kB 0.00 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.production.min.js +1.02% 16.34 kB 16.51 kB +0.89% 6.06 kB 6.11 kB
oss-stable/react-server/cjs/react-server-flight.production.min.js +1.02% 16.34 kB 16.51 kB +0.89% 6.06 kB 6.11 kB
facebook-www/ReactFlightDOMServer-prod.modern.js +0.83% 39.51 kB 39.83 kB +0.78% 8.98 kB 9.05 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.min.js +0.62% 26.90 kB 27.07 kB +0.58% 9.29 kB 9.34 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.min.js +0.62% 26.90 kB 27.07 kB +0.58% 9.29 kB 9.34 kB
oss-stable-semver/react-server-dom-turbopack/umd/react-server-dom-turbopack-server.browser.production.min.js +0.59% 28.24 kB 28.41 kB +0.55% 9.35 kB 9.40 kB
oss-stable/react-server-dom-turbopack/umd/react-server-dom-turbopack-server.browser.production.min.js +0.59% 28.24 kB 28.41 kB +0.55% 9.35 kB 9.40 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.min.js +0.59% 28.13 kB 28.30 kB +0.51% 9.25 kB 9.29 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.min.js +0.59% 28.13 kB 28.30 kB +0.51% 9.25 kB 9.29 kB
oss-stable-semver/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.production.min.js +0.58% 28.76 kB 28.93 kB +0.62% 9.49 kB 9.54 kB
oss-stable/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.production.min.js +0.58% 28.76 kB 28.93 kB +0.62% 9.49 kB 9.54 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.min.js +0.58% 28.65 kB 28.82 kB +0.50% 9.41 kB 9.45 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.min.js +0.58% 28.65 kB 28.82 kB +0.50% 9.41 kB 9.45 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.min.js +0.58% 28.66 kB 28.82 kB +0.62% 9.37 kB 9.43 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.min.js +0.58% 28.66 kB 28.82 kB +0.62% 9.37 kB 9.43 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.min.js +0.57% 29.06 kB 29.22 kB +0.55% 9.51 kB 9.56 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.min.js +0.57% 29.06 kB 29.22 kB +0.55% 9.51 kB 9.56 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.unbundled.production.min.js +0.57% 29.44 kB 29.61 kB +0.52% 9.70 kB 9.75 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.unbundled.production.min.js +0.57% 29.44 kB 29.61 kB +0.52% 9.70 kB 9.75 kB
oss-experimental/react-server/cjs/react-server-flight.production.min.js +0.56% 18.80 kB 18.90 kB +0.51% 6.72 kB 6.75 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.min.js +0.56% 29.94 kB 30.10 kB +0.55% 9.85 kB 9.91 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.min.js +0.56% 29.94 kB 30.10 kB +0.55% 9.85 kB 9.91 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.production.min.js +0.56% 30.01 kB 30.18 kB +0.52% 9.85 kB 9.90 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.production.min.js +0.56% 30.01 kB 30.18 kB +0.52% 9.85 kB 9.90 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.min.js +0.55% 30.52 kB 30.69 kB +0.53% 10.00 kB 10.06 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.min.js +0.55% 30.52 kB 30.69 kB +0.53% 10.00 kB 10.06 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.min.js +0.37% 29.41 kB 29.52 kB +0.56% 9.95 kB 10.00 kB
oss-experimental/react-server-dom-turbopack/umd/react-server-dom-turbopack-server.browser.production.min.js +0.36% 30.81 kB 30.92 kB +0.38% 10.02 kB 10.06 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.min.js +0.35% 31.23 kB 31.34 kB +0.38% 10.10 kB 10.13 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.min.js +0.35% 30.69 kB 30.80 kB +0.36% 9.93 kB 9.97 kB
oss-experimental/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.production.min.js +0.35% 31.32 kB 31.43 kB +0.26% 10.17 kB 10.20 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.min.js +0.35% 31.64 kB 31.75 kB +0.38% 10.20 kB 10.24 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.min.js +0.35% 31.22 kB 31.33 kB +0.45% 10.06 kB 10.10 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.unbundled.production.min.js +0.34% 31.95 kB 32.06 kB +0.58% 10.37 kB 10.43 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.min.js +0.34% 32.45 kB 32.55 kB +0.57% 10.52 kB 10.58 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.production.min.js +0.34% 32.52 kB 32.63 kB +0.86% 10.52 kB 10.61 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.min.js +0.33% 33.03 kB 33.14 kB +0.54% 10.70 kB 10.76 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js = 141.55 kB 141.21 kB = 32.20 kB 32.02 kB
oss-experimental/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.development.js = 142.96 kB 142.61 kB = 31.37 kB 31.23 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.development.js = 139.27 kB 138.92 kB = 31.57 kB 31.39 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.development.js = 138.92 kB 138.57 kB = 31.36 kB 31.18 kB
oss-experimental/react-server-dom-turbopack/umd/react-server-dom-turbopack-server.browser.development.js = 140.39 kB 140.04 kB = 30.68 kB 30.53 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js = 136.67 kB 136.33 kB = 31.25 kB 31.08 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.unbundled.development.js = 136.63 kB 136.28 kB = 30.76 kB 30.58 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js = 135.62 kB 135.28 kB = 30.92 kB 30.75 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.development.js = 134.74 kB 134.40 kB = 30.74 kB 30.57 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.development.js = 133.18 kB 132.83 kB = 30.23 kB 30.05 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js = 130.47 kB 130.12 kB = 29.70 kB 29.52 kB
facebook-www/ReactFlightDOMServer-dev.modern.js = 101.48 kB 101.21 kB = 21.15 kB 21.04 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js = 119.87 kB 119.52 kB = 27.89 kB 27.79 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js = 119.87 kB 119.52 kB = 27.89 kB 27.79 kB
oss-stable-semver/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.development.js = 120.46 kB 120.11 kB = 27.07 kB 26.96 kB
oss-stable/react-server-dom-webpack/umd/react-server-dom-webpack-server.browser.development.js = 120.46 kB 120.11 kB = 27.07 kB 26.96 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.development.js = 117.58 kB 117.24 kB = 27.28 kB 27.17 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.development.js = 117.58 kB 117.24 kB = 27.28 kB 27.17 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.development.js = 117.23 kB 116.89 kB = 27.00 kB 26.91 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.development.js = 117.23 kB 116.89 kB = 27.00 kB 26.91 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js = 116.52 kB 116.18 kB = 27.19 kB 27.09 kB
oss-stable-semver/react-server-dom-turbopack/umd/react-server-dom-turbopack-server.browser.development.js = 117.89 kB 117.54 kB = 26.38 kB 26.28 kB
oss-stable/react-server-dom-turbopack/umd/react-server-dom-turbopack-server.browser.development.js = 117.89 kB 117.54 kB = 26.38 kB 26.28 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.unbundled.development.js = 114.94 kB 114.60 kB = 26.44 kB 26.34 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.unbundled.development.js = 114.94 kB 114.60 kB = 26.44 kB 26.34 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js = 114.51 kB 114.17 kB = 26.79 kB 26.70 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js = 114.51 kB 114.17 kB = 26.79 kB 26.70 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js = 114.14 kB 113.79 kB = 26.73 kB 26.62 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.development.js = 114.14 kB 113.79 kB = 26.73 kB 26.62 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js = 114.45 kB 114.10 kB = 26.64 kB 26.53 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.production.js = 113.86 kB 113.52 kB = 26.30 kB 26.19 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.development.js = 112.58 kB 112.24 kB = 26.29 kB 26.19 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.development.js = 112.58 kB 112.24 kB = 26.29 kB 26.19 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.development.js = 111.69 kB 111.35 kB = 26.06 kB 25.95 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.development.js = 111.69 kB 111.35 kB = 26.06 kB 25.95 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.unbundled.production.js = 111.79 kB 111.44 kB = 25.81 kB 25.70 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js = 111.34 kB 110.99 kB = 26.24 kB 26.14 kB
oss-experimental/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js = 111.02 kB 110.67 kB = 26.17 kB 26.06 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js = 109.61 kB 109.27 kB = 25.80 kB 25.70 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js = 108.78 kB 108.43 kB = 25.47 kB 25.37 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js = 108.78 kB 108.43 kB = 25.47 kB 25.37 kB
oss-experimental/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js = 108.77 kB 108.42 kB = 25.56 kB 25.45 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js = 106.36 kB 106.02 kB = 24.99 kB 24.90 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.production.js = 106.36 kB 106.02 kB = 24.99 kB 24.90 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.js = 105.64 kB 105.29 kB = 24.80 kB 24.69 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js = 104.29 kB 103.95 kB = 24.45 kB 24.36 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.production.js = 104.29 kB 103.95 kB = 24.45 kB 24.36 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.production.js = 103.71 kB 103.36 kB = 24.11 kB 24.02 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.unbundled.production.js = 103.71 kB 103.36 kB = 24.11 kB 24.02 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.unbundled.production.js = 101.63 kB 101.28 kB = 23.61 kB 23.52 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.node.unbundled.production.js = 101.63 kB 101.28 kB = 23.61 kB 23.52 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js = 100.73 kB 100.38 kB = 23.87 kB 23.78 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.production.js = 100.73 kB 100.38 kB = 23.87 kB 23.78 kB
oss-stable-semver/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js = 100.41 kB 100.06 kB = 23.81 kB 23.72 kB
oss-stable/react-server-dom-webpack/cjs/react-server-dom-webpack-server.browser.production.js = 100.41 kB 100.06 kB = 23.81 kB 23.72 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js = 99.00 kB 98.66 kB = 23.44 kB 23.34 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.edge.production.js = 99.00 kB 98.66 kB = 23.44 kB 23.34 kB
oss-stable-semver/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js = 98.16 kB 97.81 kB = 23.21 kB 23.11 kB
oss-stable/react-server-dom-turbopack/cjs/react-server-dom-turbopack-server.browser.production.js = 98.16 kB 97.81 kB = 23.21 kB 23.11 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.js = 95.48 kB 95.13 kB = 22.63 kB 22.54 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.js = 95.48 kB 95.13 kB = 22.63 kB 22.54 kB
oss-experimental/react-server/cjs/react-server-flight.development.js = 93.16 kB 92.82 kB = 21.15 kB 21.00 kB
oss-stable-semver/react-server/cjs/react-server-flight.development.js = 72.35 kB 72.01 kB = 17.23 kB 17.11 kB
oss-stable/react-server/cjs/react-server-flight.development.js = 72.35 kB 72.01 kB = 17.23 kB 17.11 kB
oss-experimental/react-server/cjs/react-server-flight.production.js = 69.23 kB 68.88 kB = 16.55 kB 16.43 kB
oss-stable-semver/react-server/cjs/react-server-flight.production.js = 59.30 kB 58.95 kB = 14.46 kB 14.34 kB
oss-stable/react-server/cjs/react-server-flight.production.js = 59.30 kB 58.95 kB = 14.46 kB 14.34 kB
test_utils/ReactAllWarnings.js Deleted 65.49 kB 0.00 kB Deleted 16.16 kB 0.00 kB

Generated by 🚫 dangerJS against 3b41b85

@unstubbable unstubbable changed the title [Flight] Choose lazy or value ID based on whether model chunk was emitted [Flight] Use lazy ID if referenced model chunk was not emitted yet Mar 23, 2024
The added test, intended to fail and reproduce the [reported
issue](facebook#28595), unexpectedly
passes in its current state. I see three possible reasons:

1. The bug report could be invalid.
2. How I've structured the test might be insufficient to replicate what
   `ai/rsc` is doing.
3. Something in the test setup could be masking the actual error.
   (Maybe related to fake timers?)

If the problem lies in reason 2 or 3, this test could possibly serve as
a foundation for further investigation.
Copy link
Collaborator

@sebmarkbage sebmarkbage left a comment

Choose a reason for hiding this comment

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

Perf wise this isn't the best solution and there's other techniques we can use.

However, the general idea is that this shouldn't matter because the Client is supposed to be resilient to this kind of formatting. I believe the only case it's not is when it's at the root and it might be solvable on the client instead.

@unstubbable
Copy link
Contributor Author

Perf wise this isn't the best solution and there's other techniques we can use.

However, the general idea is that this shouldn't matter because the Client is supposed to be resilient to this kind of formatting.

Are you suggesting ReactFlightClient should treat $5 as a lazy reference, as it would for $L5, if it detects that chunk 5 has not been received/processed yet?

I believe the only case it's not is when it's at the root and it might be solvable on the client instead.

When what is at the root? The async server component? Or the suspense boundary with the current/next promises? The issue is also reproducible with this:

const {pipe} = ReactServerDOMServer.renderToPipeableStream(
  <main>{suspendedChunk.row}</main>,
  webpackMap,
);

...or by nesting the server component into any other elements. I guess you mean something else entirely, with "at the root"?

@unstubbable
Copy link
Contributor Author

unstubbable commented Mar 25, 2024

d8984de <- That's not what you meant, is it?
(It does also pass the added test. But I would suspect that this simple fix might create unnecessary lazy wrappers in other scenarios?)

@sebmarkbage
Copy link
Collaborator

This should be able to lazily resolve a reference that hasn't resolved yet when the parent is. This can happen for other reasons too like if a dependency on a Client Reference hasn't resolve yet.

https://github.com/facebook/react/blob/main/packages/react-client/src/ReactFlightClient.js#L586

However, this works by mutating the parent object. The parent object might be the root object that JSON.parse creates. It's an object with a single property named the empty string. In that case this technique doesn't work.

@unstubbable
Copy link
Contributor Author

unstubbable commented Mar 27, 2024

Hm, ok, I think I understand. This change in the Flight Client would also make the test pass, and works with the root object.

But not using blocked.value at all anymore doesn't look right to me, and I'd like to falsify this somehow. Unfortunately, I wasn't able to do that. (UPDATE: 3b41b85 shows that it's not a proper solution.) Apparently there are currently only two other tests that cover createModelResolver():

  • ReactFlightDOM › should be able to recover from a direct reference erroring client-side async
  • ReactFlight › can transport cyclic objects

And in these tests the affected chunks are either BLOCKED or CYCLIC, but not PENDING. Furthermore, the proposed change works for them as well.

I've also checked the Flight Fixture, but createModelResolver() is not covered by those examples, it seems.

If you have a very specific fix in mind, feel free to go ahead and create a PR for that, if you prefer that. I don't want to steal your time and attention from more important things, by having to answer here. I'm just doing this because it's a fun challenge trying to solve the bug I stumbled upon.

Final thought: Regardless of whether the Flight Client should be resilient to the order of the chunks, I think it still makes sense to emit references to not emitted models with a lazy ID in the Flight Server. As I've already written in the issue, you also mentioned this in the TODO comment:

We should really detect whether this already was emitted and synchronously available. In that case we can refer to it synchronously and only make it lazy otherwise.

I would also be interested in what other technique we could use that would be better performing than using the emittedModelChunkIds set.

Thanks for reading this!

@unstubbable
Copy link
Contributor Author

However, the general idea is that this shouldn't matter because the Client is supposed to be resilient to this kind of formatting.

Maybe I misinterpreted this sentence. Do you plan to (eventually) get rid of the lazy IDs completely, and handle all those cases in the client instead (without creating lazy wrappers)?

@unstubbable
Copy link
Contributor Author

unstubbable commented Mar 29, 2024

This should be able to lazily resolve a reference that hasn't resolved yet when the parent is. This can happen for other reasons too like if a dependency on a Client Reference hasn't resolve yet.

https://github.com/facebook/react/blob/main/packages/react-client/src/ReactFlightClient.js#L586

However, this works by mutating the parent object. The parent object might be the root object that JSON.parse creates. It's an object with a single property named the empty string. In that case this technique doesn't work.

I've debugged this now extensively, and I believe that there's more issues with this lazy resolving technique than the root object issue.

When I wrap the chunk.value inside of Row 1 with an element (used a div here for simplicity):

Screenshot 2024-03-29 at 13 18 42

...the root object issue is prevented, but the lazy resolution still does not work (failing with a different error now). It seems that blocked.value is always sometimes stale when assigning it as chunk value:

Screenshot 2024-03-29 at 13 11 58

Using chunk 6 as an example here, we can see that the resolved suspense element props (via chunk 7) are correctly assigned to the object of chunk 6 (parent object of $7). But the blocked value (the suspense element) still has null props when it is assigned as chunk value to chunk 6.

(And when dealing with the root object, blocked.value is a stale null.)

I am still thinking about how the problem can be solved without resorting to returning lazy chunk wrappers for pending chunks in the default case of parseModelString...


The added unit test also shows another potential issue here: In production mode, the async server component elements are emitted twice:

1:"$Sreact.suspense"
0:["$","$1",null,{"fallback":["$","p",null,{"children":"loading"}],"children":"$L2"}]
2:["$","$1",null,{"fallback":["$","$1",null,{"fallback":["$","p",null,{"children":"loading posts and photos"}],"children":["$L3","$L4"]}],"children":"$L5"}]
9:{"children":"loading posts and photos"}
8:["$","p",null,"$9"]
a:["$b","$c"]
7:{"fallback":"$8","children":"$a"}
6:["$","$1",null,"$7"]
5:["$","div",null,{"children":"$6"}]
3:["$","div",null,{"children":"posts"}]
b:["$","div",null,{"children":"posts"}]
4:["$","div",null,{"children":"photos"}]
c:["$","div",null,{"children":"photos"}]

Whereas in dev mode, due to the outlineTask call for assigning debug info, they are emitted only once:

1:"$Sreact.suspense"
2:D{"name":"Row","env":"Server"}
0:["$","$1",null,{"fallback":["$","p",null,{"children":"loading"}],"children":"$L2"}]
3:D{"name":"DelayedText","env":"Server"}
4:D{"name":"DelayedText","env":"Server"}
5:D{"name":"Row","env":"Server"}
2:["$","$1",null,{"fallback":["$","$1",null,{"fallback":["$","p",null,{"children":"loading posts and photos"}],"children":["$L3","$L4"]}],"children":"$L5"}]
9:{"children":"loading posts and photos"}
8:["$","p",null,"$9"]
a:["$3","$4"]
7:{"fallback":"$8","children":"$a"}
6:["$","$1",null,"$7"]
5:["$","div",null,{"children":"$6"}]
3:["$","div",null,{"children":"posts"}]
4:["$","div",null,{"children":"photos"}]

Footnotes

  1. I am aware that the names "chunk" and "row" are confusing in this context, since they are also used in the Flight Client for the RSC rows. This was just derived from the abbreviated version in ai/rsc.

@unstubbable
Copy link
Contributor Author

After having written all of this, this potential (and maybe naïve) solution materialised in front of my eyes: #28669

sebmarkbage pushed a commit that referenced this pull request Apr 1, 2024
Alternative to #28620.

Instead of emitting lazy references to not-yet-emitted models in the
Flight Server, this fixes the observed issue in
unstubbable/ai-rsc-test#1 by adjusting the lazy
model resolution in the Flight Client to update stale blocked root
models, before assigning them as chunk values. In addition, the element
props are not outlined anymore in the Flight Server to avoid having to
also handle their staleness in blocked elements.

fixes #28595
github-actions bot pushed a commit that referenced this pull request Apr 1, 2024
Alternative to #28620.

Instead of emitting lazy references to not-yet-emitted models in the
Flight Server, this fixes the observed issue in
unstubbable/ai-rsc-test#1 by adjusting the lazy
model resolution in the Flight Client to update stale blocked root
models, before assigning them as chunk values. In addition, the element
props are not outlined anymore in the Flight Server to avoid having to
also handle their staleness in blocked elements.

fixes #28595

DiffTrain build for [93f9179](93f9179)
@unstubbable
Copy link
Contributor Author

Closing for now, as the bug has been fixed with #28669. Using lazy IDs for these model references might still be something we would want to do, but maybe there's a better technique to implement this.

@unstubbable unstubbable closed this Apr 2, 2024
@unstubbable unstubbable deleted the fix-28595 branch April 2, 2024 08:13
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
…ok#28669)

Alternative to facebook#28620.

Instead of emitting lazy references to not-yet-emitted models in the
Flight Server, this fixes the observed issue in
unstubbable/ai-rsc-test#1 by adjusting the lazy
model resolution in the Flight Client to update stale blocked root
models, before assigning them as chunk values. In addition, the element
props are not outlined anymore in the Flight Server to avoid having to
also handle their staleness in blocked elements.

fixes facebook#28595
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bug: [Flight] Async server components in ai/rsc not rendered correctly
4 participants