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

Invariant that throws when committing wrong tree #15517

Merged
merged 1 commit into from
May 13, 2019

Conversation

acdlite
Copy link
Collaborator

@acdlite acdlite commented Apr 26, 2019

If React finishes rendering a tree, delays committing it (e.g. Suspense), then subsequently starts over or renders a new tree, the pending tree is no longer valid. That's because rendering a new work-in progress mutates the old one in place.

The current structure of the work loop makes this hard to reason about because, although renderRoot and commitRoot are separate functions, they can't be interleaved. If they are interleaved by accident, it either results in inconsistent render output or invariant violations that are hard to debug.

This commit adds an invariant that throws if the new tree is the same as the old one. This won't prevent all bugs of this class, but it should catch the most common kind.

To implement the invariant, I store the finished tree on a field on the root. We already had a field for this, but it was only being used for the unstable createBatch feature.

A more rigorous way to address this type of problem could be to unify renderRoot and commitRoot into a single function, so that it's harder to accidentally interleave the two phases. I plan to do something like this in a follow-up.

If React finishes rendering a tree, delays committing it (e.g.
Suspense), then subsequently starts over or renders a new tree, the
pending tree is no longer valid. That's because rendering a new work-in
progress mutates the old one in place.

The current structure of the work loop makes this hard to reason about
because, although `renderRoot` and `commitRoot` are separate functions,
they can't be interleaved. If they are interleaved by accident, it
either results in inconsistent render output or invariant violations
that are hard to debug.

This commit adds an invariant that throws if the new tree is the same as
the old one. This won't prevent all bugs of this class, but it should
catch the most common kind.

To implement the invariant, I store the finished tree on a field on the
root. We already had a field for this, but it was only being used for
the unstable `createBatch` feature.

A more rigorous way to address this type of problem could be to unify
`renderRoot` and `commitRoot` into a single function, so that it's
harder to accidentally interleave the two phases. I plan to do something
like this in a follow-up.
@sizebot
Copy link

sizebot commented Apr 26, 2019

ReactDOM: size: 🔺+0.1%, gzip: 🔺+0.1%

Details of bundled changes.

Comparing: c4d1dcb...a592c8a

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom.development.js 0.0% +0.1% 819.56 KB 819.83 KB 186.62 KB 186.72 KB UMD_DEV
react-dom.production.min.js 🔺+0.1% 🔺+0.1% 103.68 KB 103.8 KB 33.69 KB 33.71 KB UMD_PROD
react-dom.profiling.min.js +0.1% +0.1% 106.83 KB 106.95 KB 34.67 KB 34.71 KB UMD_PROFILING
react-dom.development.js 0.0% +0.1% 814.04 KB 814.31 KB 185.08 KB 185.18 KB NODE_DEV
react-dom.production.min.js 🔺+0.1% 0.0% 103.67 KB 103.8 KB 33.13 KB 33.15 KB NODE_PROD
react-dom.profiling.min.js +0.1% +0.1% 107 KB 107.13 KB 33.98 KB 34.01 KB NODE_PROFILING
ReactDOM-dev.js 0.0% +0.1% 838.45 KB 838.73 KB 186.54 KB 186.64 KB FB_WWW_DEV
ReactDOM-prod.js 🔺+0.1% 🔺+0.1% 344.54 KB 344.73 KB 63.77 KB 63.81 KB FB_WWW_PROD
ReactDOM-profiling.js +0.1% +0.1% 349.93 KB 350.12 KB 64.76 KB 64.81 KB FB_WWW_PROFILING
react-dom-unstable-fire.development.js 0.0% +0.1% 819.88 KB 820.16 KB 186.76 KB 186.85 KB UMD_DEV
react-dom-unstable-fire.production.min.js 🔺+0.1% 🔺+0.1% 103.69 KB 103.82 KB 33.7 KB 33.71 KB UMD_PROD
react-dom-unstable-fire.profiling.min.js +0.1% +0.1% 106.84 KB 106.97 KB 34.68 KB 34.72 KB UMD_PROFILING
react-dom-unstable-fire.development.js 0.0% +0.1% 814.36 KB 814.64 KB 185.21 KB 185.31 KB NODE_DEV
react-dom-unstable-fire.production.min.js 🔺+0.1% 0.0% 103.69 KB 103.81 KB 33.14 KB 33.16 KB NODE_PROD
react-dom-unstable-fire.profiling.min.js +0.1% +0.1% 107.01 KB 107.14 KB 33.99 KB 34.02 KB NODE_PROFILING
ReactFire-dev.js 0.0% +0.1% 837.64 KB 837.92 KB 186.53 KB 186.63 KB FB_WWW_DEV
ReactFire-prod.js 🔺+0.1% 0.0% 332.54 KB 332.72 KB 61.37 KB 61.38 KB FB_WWW_PROD
ReactFire-profiling.js +0.1% +0.1% 337.9 KB 338.08 KB 62.34 KB 62.38 KB FB_WWW_PROFILING
react-dom-test-utils.production.min.js 0.0% 🔺+0.1% 10.56 KB 10.56 KB 3.89 KB 3.89 KB UMD_PROD
react-dom-test-utils.development.js 0.0% 0.0% 53.95 KB 53.95 KB 14.91 KB 14.91 KB NODE_DEV
react-dom-unstable-native-dependencies.development.js 0.0% -0.0% 60.76 KB 60.76 KB 15.85 KB 15.84 KB UMD_DEV
react-dom-server.browser.production.min.js 0.0% -0.0% 19.12 KB 19.12 KB 7.21 KB 7.21 KB UMD_PROD
react-dom-server.node.production.min.js 0.0% 0.0% 19.91 KB 19.91 KB 7.5 KB 7.5 KB NODE_PROD
react-dom-unstable-fizz.browser.development.js 0.0% +0.1% 3.66 KB 3.66 KB 1.45 KB 1.45 KB UMD_DEV
react-dom-unstable-fizz.browser.production.min.js 0.0% 🔺+0.1% 1.21 KB 1.21 KB 705 B 706 B UMD_PROD
react-dom-unstable-fizz.browser.production.min.js 0.0% 🔺+0.2% 1.05 KB 1.05 KB 636 B 637 B NODE_PROD

react-art

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-art.development.js 0.0% +0.1% 557.16 KB 557.44 KB 121.69 KB 121.79 KB UMD_DEV
react-art.production.min.js 🔺+0.1% 🔺+0.1% 95.51 KB 95.64 KB 29.3 KB 29.33 KB UMD_PROD
react-art.development.js +0.1% +0.1% 488.19 KB 488.46 KB 104.3 KB 104.4 KB NODE_DEV
react-art.production.min.js 🔺+0.2% 🔺+0.1% 60.49 KB 60.62 KB 18.63 KB 18.65 KB NODE_PROD
ReactART-dev.js +0.1% +0.1% 497.3 KB 497.58 KB 103.38 KB 103.48 KB FB_WWW_DEV
ReactART-prod.js 🔺+0.1% 🔺+0.1% 194.78 KB 194.96 KB 33.13 KB 33.18 KB FB_WWW_PROD

react-native-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
ReactNativeRenderer-dev.js 0.0% +0.1% 628.66 KB 628.94 KB 133.52 KB 133.63 KB RN_FB_DEV
ReactNativeRenderer-prod.js 🔺+0.1% 🔺+0.2% 243.57 KB 243.81 KB 42.28 KB 42.35 KB RN_FB_PROD
ReactNativeRenderer-profiling.js +0.1% +0.1% 251.82 KB 252.06 KB 43.89 KB 43.95 KB RN_FB_PROFILING
ReactNativeRenderer-dev.js 0.0% +0.1% 628.57 KB 628.85 KB 133.48 KB 133.59 KB RN_OSS_DEV
ReactNativeRenderer-prod.js 🔺+0.1% 🔺+0.2% 243.58 KB 243.82 KB 42.28 KB 42.34 KB RN_OSS_PROD
ReactNativeRenderer-profiling.js +0.1% +0.1% 251.83 KB 252.08 KB 43.89 KB 43.95 KB RN_OSS_PROFILING
ReactFabric-dev.js 0.0% +0.1% 617.34 KB 617.62 KB 130.8 KB 130.91 KB RN_FB_DEV
ReactFabric-prod.js 🔺+0.1% 🔺+0.2% 236.69 KB 236.93 KB 40.96 KB 41.02 KB RN_FB_PROD
ReactFabric-profiling.js +0.1% +0.2% 244.93 KB 245.17 KB 42.62 KB 42.69 KB RN_FB_PROFILING
ReactFabric-dev.js 0.0% +0.1% 617.25 KB 617.53 KB 130.77 KB 130.88 KB RN_OSS_DEV
ReactFabric-prod.js 🔺+0.1% 🔺+0.1% 236.69 KB 236.93 KB 40.95 KB 41.01 KB RN_OSS_PROD
ReactFabric-profiling.js +0.1% +0.2% 244.94 KB 245.19 KB 42.62 KB 42.68 KB RN_OSS_PROFILING

react-reconciler

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-reconciler.development.js +0.1% +0.1% 485.72 KB 486 KB 102.74 KB 102.84 KB NODE_DEV
react-reconciler.production.min.js 🔺+0.2% 🔺+0.1% 61.42 KB 61.55 KB 18.37 KB 18.4 KB NODE_PROD
react-reconciler-persistent.development.js +0.1% +0.1% 483.63 KB 483.9 KB 101.86 KB 101.96 KB NODE_DEV
react-reconciler-persistent.production.min.js 🔺+0.2% 🔺+0.1% 61.44 KB 61.56 KB 18.38 KB 18.4 KB NODE_PROD

Generated by 🚫 dangerJS

@gaearon
Copy link
Collaborator

gaearon commented Apr 27, 2019

Was it expected to actually fix #15512? I still see the number glitching but with this change, I can't get neither the invariant nor the warning to trigger anymore.

@gaearon
Copy link
Collaborator

gaearon commented Apr 27, 2019

However, I am also not seeing commitRoot and renderRoot being interleaved on master when the error happens:

Screen Shot 2019-04-27 at 02 16 28

Screen Shot 2019-04-27 at 02 16 40

Screen Shot 2019-04-27 at 02 17 25

Does this mean the root cause is different, or is my method flawed?

@acdlite
Copy link
Collaborator Author

acdlite commented Apr 27, 2019

It fixes it but I didn’t isolate it to a test case yet. I know what’s happening at a high level but I don’t know what specific sequence of events needs to happen to trigger it. I’ll look into it more on Monday.

@acdlite acdlite merged commit cc24d0e into facebook:master May 13, 2019
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.

None yet

5 participants