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

[Fizz] Track postponed holes in the prerender pass #27317

Merged
merged 10 commits into from
Aug 31, 2023

Conversation

sebmarkbage
Copy link
Collaborator

@sebmarkbage sebmarkbage commented Aug 30, 2023

This is basically the implementation for the prerender pass.

Instead of forking basically the whole implementation for prerender, I just add a conditional field on the request. If it's null it behaves like before. If it's non-null then instead of triggering client rendered boundaries it triggers those into a "postponed" state which is basically just a variant of "pending". It's supposed to be filled in later.

It also builds up a serializable tree of which path can be followed to find the holes. This is basically a reverse KeyPath tree.

It is unfortunate that this approach adds more code to the regular Fizz builds but in practice. It seems like this side is not going to add much code and we might instead just want to merge the builds so that it's smaller when you have prerender and resume in the same bundle - which I think will be common in practice.

This just implements the prerender side, and not the resume side, which is why the tests have a TODO. That's in a follow up PR.

@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Aug 30, 2023
@react-sizebot
Copy link

react-sizebot commented Aug 30, 2023

Comparing: 3808b01...7733a15

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 = 165.63 kB 165.63 kB = 51.88 kB 51.88 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 174.70 kB 174.70 kB = 54.61 kB 54.61 kB
facebook-www/ReactDOM-prod.classic.js = 570.44 kB 570.44 kB = 100.45 kB 100.45 kB
facebook-www/ReactDOM-prod.modern.js = 554.21 kB 554.21 kB = 97.61 kB 97.61 kB
oss-experimental/react-dom/server.node.js +6.93% 0.69 kB 0.74 kB +1.12% 0.27 kB 0.27 kB
oss-stable-semver/react-dom/server.node.js +6.93% 0.69 kB 0.74 kB +1.12% 0.27 kB 0.27 kB
oss-stable/react-dom/server.node.js +6.93% 0.69 kB 0.74 kB +1.12% 0.27 kB 0.27 kB
oss-experimental/react-server/cjs/react-server.production.min.js +6.16% 26.36 kB 27.99 kB +4.68% 8.96 kB 9.38 kB
oss-experimental/react-server/cjs/react-server.development.js +3.95% 154.42 kB 160.52 kB +3.21% 38.71 kB 39.95 kB
oss-experimental/react-dom/cjs/react-dom-static.node.production.min.js +2.34% 69.76 kB 71.39 kB +1.91% 21.81 kB 22.22 kB
oss-experimental/react-dom/cjs/react-dom-static.browser.production.min.js +2.29% 65.23 kB 66.73 kB +1.95% 20.25 kB 20.64 kB
oss-experimental/react-dom/cjs/react-dom-static.edge.production.min.js +2.28% 65.56 kB 67.05 kB +1.89% 20.38 kB 20.76 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-experimental/react-dom/server.node.js +6.93% 0.69 kB 0.74 kB +1.12% 0.27 kB 0.27 kB
oss-stable-semver/react-dom/server.node.js +6.93% 0.69 kB 0.74 kB +1.12% 0.27 kB 0.27 kB
oss-stable/react-dom/server.node.js +6.93% 0.69 kB 0.74 kB +1.12% 0.27 kB 0.27 kB
oss-experimental/react-server/cjs/react-server.production.min.js +6.16% 26.36 kB 27.99 kB +4.68% 8.96 kB 9.38 kB
oss-experimental/react-server/cjs/react-server.development.js +3.95% 154.42 kB 160.52 kB +3.21% 38.71 kB 39.95 kB
oss-experimental/react-dom/cjs/react-dom-static.node.production.min.js +2.34% 69.76 kB 71.39 kB +1.91% 21.81 kB 22.22 kB
oss-experimental/react-dom/cjs/react-dom-static.browser.production.min.js +2.29% 65.23 kB 66.73 kB +1.95% 20.25 kB 20.64 kB
oss-experimental/react-dom/cjs/react-dom-static.edge.production.min.js +2.28% 65.56 kB 67.05 kB +1.89% 20.38 kB 20.76 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.production.min.js +1.94% 68.36 kB 69.69 kB +1.55% 20.99 kB 21.32 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.production.min.js +1.90% 69.94 kB 71.27 kB +1.55% 21.27 kB 21.60 kB
oss-experimental/react-dom/cjs/react-dom-server.edge.production.min.js +1.89% 70.33 kB 71.66 kB +1.55% 21.87 kB 22.21 kB
oss-experimental/react-dom/cjs/react-dom-server.node.production.min.js +1.89% 70.48 kB 71.81 kB +1.65% 21.95 kB 22.31 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +1.83% 64.95 kB 66.14 kB +1.62% 19.62 kB 19.94 kB
oss-experimental/react-dom/umd/react-dom-server-legacy.browser.production.min.js +1.82% 65.11 kB 66.30 kB +1.61% 20.01 kB 20.33 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.production.min.js +1.80% 66.03 kB 67.22 kB +1.61% 20.38 kB 20.71 kB
oss-experimental/react-dom/umd/react-dom-server.browser.production.min.js +1.80% 66.17 kB 67.36 kB +1.49% 20.65 kB 20.96 kB
oss-experimental/react-dom/cjs/react-dom-static.browser.development.js +1.69% 358.23 kB 364.30 kB +1.54% 80.61 kB 81.86 kB
oss-experimental/react-dom/cjs/react-dom-static.edge.development.js +1.69% 358.64 kB 364.71 kB +1.53% 80.74 kB 81.98 kB
oss-experimental/react-dom/cjs/react-dom-static.node.development.js +1.68% 360.38 kB 366.44 kB +1.54% 80.93 kB 82.18 kB
oss-stable-semver/react-server/cjs/react-server.production.min.js +1.54% 25.52 kB 25.91 kB +1.25% 8.67 kB 8.78 kB
oss-stable/react-server/cjs/react-server.production.min.js +1.54% 25.52 kB 25.91 kB +1.25% 8.67 kB 8.78 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.development.js +1.54% 356.59 kB 362.08 kB +1.40% 80.06 kB 81.19 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.development.js +1.53% 359.15 kB 364.63 kB +1.39% 80.55 kB 81.67 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.development.js +1.52% 360.77 kB 366.25 kB +1.39% 80.83 kB 81.95 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.development.js +1.52% 361.06 kB 366.54 kB +1.38% 81.01 kB 82.12 kB
oss-experimental/react-dom/cjs/react-dom-server.edge.development.js +1.52% 361.18 kB 366.66 kB +1.39% 80.96 kB 82.08 kB
oss-experimental/react-dom/cjs/react-dom-server.node.development.js +1.52% 361.70 kB 367.19 kB +1.38% 80.88 kB 82.00 kB
oss-experimental/react-dom/umd/react-dom-server-legacy.browser.development.js +1.51% 376.33 kB 382.03 kB +1.38% 81.38 kB 82.50 kB
oss-experimental/react-dom/umd/react-dom-server.browser.development.js +1.51% 378.04 kB 383.74 kB +1.38% 81.69 kB 82.82 kB
oss-stable-semver/react-server/cjs/react-server.development.js +0.77% 148.43 kB 149.57 kB +0.75% 36.97 kB 37.25 kB
oss-stable/react-server/cjs/react-server.development.js +0.77% 148.43 kB 149.57 kB +0.75% 36.97 kB 37.25 kB
oss-experimental/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.min.js +0.23% 1.30 kB 1.31 kB +0.33% 0.62 kB 0.62 kB
oss-stable-semver/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.min.js +0.23% 1.30 kB 1.31 kB +0.33% 0.62 kB 0.62 kB
oss-stable/react-noop-renderer/cjs/react-noop-renderer-flight-server.production.min.js +0.23% 1.30 kB 1.31 kB +0.33% 0.62 kB 0.62 kB

Generated by 🚫 dangerJS against 7733a15

Comment on lines +6239 to +6247
const b = new Stream.PassThrough();
b.setEncoding('utf8');
b.on('data', chunk => {
writable.write(chunk);
});

await act(() => {
resumed.pipe(writable);
});
Copy link
Collaborator

Choose a reason for hiding this comment

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

did you mean to pipe to b or is the passthrough not necessary?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I intentionally do this so that it still closes the writable at the end. The purpose of b is to just exclude the close().

I renamed startWork to startRender and added a startPrerender. Later,
we'll probably add a startResume.

This just adds a non-null Map. This is a signal to the runtime to track
postponed holes.

Ideally this would be static but it's not worth the forking that would
require. Especially since this is just a cost when you postpone other
than a bit extra code.
Todo this we need to stash the rootFormatContext on the request. It's
unfortunate that this is state that's not needed for regular rendering.
This is unfortunate that we need to add this to the render path in general
but this doesn't really cost anything extra in terms of allocations since
this object would live anyway so it's an extra field.
For now this only tracks inside boundaries but it's set up to handle holes
in the root too.

We now need a status field on the boundary because pendingTasks === 0 no
longer implies that a boundary is complete.
This creates a tree that we can follow when replaying to find those holes.
This flips the tree and encodes the ids at this point.
Because we don't flush the stream until later after we've returned we need
to assign these points an ID early so that we can write them in the resume
payload.
Since we're now assigning IDs eagerly to postponed boundaries and segments
we can encode them into their serialized form eagerly. Instead of doing
a second pass at the end.
@sebmarkbage sebmarkbage merged commit b70a0d7 into facebook:main Aug 31, 2023
2 checks passed
github-actions bot pushed a commit that referenced this pull request Aug 31, 2023
This is basically the implementation for the prerender pass.

Instead of forking basically the whole implementation for prerender, I
just add a conditional field on the request. If it's `null` it behaves
like before. If it's non-`null` then instead of triggering client
rendered boundaries it triggers those into a "postponed" state which is
basically just a variant of "pending". It's supposed to be filled in
later.

It also builds up a serializable tree of which path can be followed to
find the holes. This is basically a reverse `KeyPath` tree.

It is unfortunate that this approach adds more code to the regular Fizz
builds but in practice. It seems like this side is not going to add much
code and we might instead just want to merge the builds so that it's
smaller when you have `prerender` and `resume` in the same bundle -
which I think will be common in practice.

This just implements the prerender side, and not the resume side, which
is why the tests have a TODO. That's in a follow up PR.

DiffTrain build for [b70a0d7](b70a0d7)
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
This is basically the implementation for the prerender pass.

Instead of forking basically the whole implementation for prerender, I
just add a conditional field on the request. If it's `null` it behaves
like before. If it's non-`null` then instead of triggering client
rendered boundaries it triggers those into a "postponed" state which is
basically just a variant of "pending". It's supposed to be filled in
later.

It also builds up a serializable tree of which path can be followed to
find the holes. This is basically a reverse `KeyPath` tree.

It is unfortunate that this approach adds more code to the regular Fizz
builds but in practice. It seems like this side is not going to add much
code and we might instead just want to merge the builds so that it's
smaller when you have `prerender` and `resume` in the same bundle -
which I think will be common in practice.

This just implements the prerender side, and not the resume side, which
is why the tests have a TODO. That's in a follow up PR.
bigfootjon pushed a commit that referenced this pull request Apr 18, 2024
This is basically the implementation for the prerender pass.

Instead of forking basically the whole implementation for prerender, I
just add a conditional field on the request. If it's `null` it behaves
like before. If it's non-`null` then instead of triggering client
rendered boundaries it triggers those into a "postponed" state which is
basically just a variant of "pending". It's supposed to be filled in
later.

It also builds up a serializable tree of which path can be followed to
find the holes. This is basically a reverse `KeyPath` tree.

It is unfortunate that this approach adds more code to the regular Fizz
builds but in practice. It seems like this side is not going to add much
code and we might instead just want to merge the builds so that it's
smaller when you have `prerender` and `resume` in the same bundle -
which I think will be common in practice.

This just implements the prerender side, and not the resume side, which
is why the tests have a TODO. That's in a follow up PR.

DiffTrain build for commit b70a0d7.
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.

4 participants