-
Notifications
You must be signed in to change notification settings - Fork 47k
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] Add Component Stacks to onError
and onPostpone
when in dev mode or during prerenders in prod mode
#27761
Conversation
ea259c8
to
76f3394
Compare
} catch (x) { | ||
if ( | ||
typeof x === 'object' && | ||
x !== null && | ||
typeof x.then === 'function' | ||
) { | ||
// this Lazy initializer is suspending. push a temporary frame onto the stack so it can be | ||
// popped off in spawnNewSuspendedTask. This aligns stack behavior between Lazy in element position | ||
// vs Component position. We do not want the frame for Errors so we exclusively do this in | ||
// the wakeable branch | ||
pushBuiltInComponentStackInDEV(task, 'Lazy'); | ||
} | ||
throw x; |
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 was originally motivated to remove this special case because I didn't want to have the extra try in prod. However I'm also not sure this was correct to begin with. The comment (which I wrote) indicates we are trying to have parity between stack behavior with element and Component position lazy's. However When I test the client behavior there is a seemingly buggy thing today where as an element if the lazy itself rejects you are told there is an error in component. However by removing this special case now elements and components both get a Lazy
frame if the promise rejects and if the promise resolves this frame is removed from any future errors that arise deeper in the render. I think this makes sense but I wrote this comment for a reason and I no longer remember why. For now I'm going with this until we can clarify what the proper client behavior is since it should match
function pushFunctionComponentStackInDEV(task: Task, type: Function): void { | ||
if (__DEV__) { | ||
task.componentStack = { | ||
tag: 1, | ||
parent: task.componentStack, | ||
type, | ||
}; | ||
} |
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 refactored the way we track our stacks to be more efficient since we are running this in prod now. instead of pushing and popping we always stash and restore. To make this pattern clearer I renamed to get...()
instead of push...()
and moved the task mutation into the functions where these are used. This should inline better and eliminates a null check.
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.
Arguably this is more of a create
prefix because you're not getting it from somewhere else.
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.
Since we're doing all the work when there's no error in prod, arguably we could always expose it since it only has to do the expensive computation if there's actually an error.
76f3394
to
74ccffb
Compare
74ccffb
to
9325617
Compare
… mode or during prerenders in prod mode (#27761) Historically React would produce component stacks for dev builds only. There is a cost to tracking component stacks and given the prod builds try to optimize runtime performance these stacks were left out. More recently React added production component stacks to Fiber in because it can be immensely helpful in tracking down hard to debug production issues. Fizz was not updated to have a similar behavior. With the advent of prerendering however stacks for production in Fizz are more relevant because prerendering is not really a dev-time task. If you want the ability to reason about errors or postpones that happen during a prerender having component stacks to interrogate is helpful and these component stacks need to be available in production otherwise you are really never going to see them. (it is possible that you could do dev-mode prerenders but we don't expect this to be a common dev mode workflow) To better support the prerender use case and to make error logging in Fizz more useful the following changes have been made 1. `onPostpone` now accepts a second `postponeInfo` argument which will contain a componentStack. Postpones always originate from a component render so the stack should be consistently available. The type however will indicate the stack is optional so we can remove them in the future if we decide the overhead is the wrong tradeoff in certain cases 2. `onError` now accepts a second `errorInfo` argument which may contain a componentStack. If an error originated from a component a stack will be included in the following cases. This change entails tracking the component hierarchy in prod builds now. While this isn't cost free it is implemented in a relatively lean manner. Deferring the most expensive work (reifying the stack) until we are actually in an error pathway. In the course of implementing this change a number of simplifications were made to the code which should make the stack tracking more resilient. We no longer use a module global to curry the stack up to some handler. This was delicate because you needed to always reset it properly. We now curry the stack on the task itself. Another change made was to track the component stack on SuspenseBoundary instances so that we can provide the stack when aborting suspense boundaries to help you determine which ones were affected by an abort. DiffTrain build for [63310df](63310df)
Just a heads up that this is causing a performance regression and we may need to back out or fix forward. |
…`onError` and `onPostpone` when in dev mode or during prerenders in prod mode (facebook#27761)" Summary: Backing out this change since it is the most likely culprit for a significant observed performance regression. Original commit changeset: bf4ae59939dc Original Phabricator Diffs: D52225293, D52290859 Reviewed By: josephsavona, fullstackhacker Differential Revision: D52307383
### React upstream changes - facebook/react#27888 - facebook/react#27870 - facebook/react#27871 - facebook/react#27850 - facebook/react#27839 - facebook/react#27842 - facebook/react#27841 - facebook/react#27840 - facebook/react#27761 - facebook/react#27831 - facebook/react#27801 Closes NEXT-2012
… mode or during prerenders in prod mode (facebook#27761) Historically React would produce component stacks for dev builds only. There is a cost to tracking component stacks and given the prod builds try to optimize runtime performance these stacks were left out. More recently React added production component stacks to Fiber in because it can be immensely helpful in tracking down hard to debug production issues. Fizz was not updated to have a similar behavior. With the advent of prerendering however stacks for production in Fizz are more relevant because prerendering is not really a dev-time task. If you want the ability to reason about errors or postpones that happen during a prerender having component stacks to interrogate is helpful and these component stacks need to be available in production otherwise you are really never going to see them. (it is possible that you could do dev-mode prerenders but we don't expect this to be a common dev mode workflow) To better support the prerender use case and to make error logging in Fizz more useful the following changes have been made 1. `onPostpone` now accepts a second `postponeInfo` argument which will contain a componentStack. Postpones always originate from a component render so the stack should be consistently available. The type however will indicate the stack is optional so we can remove them in the future if we decide the overhead is the wrong tradeoff in certain cases 2. `onError` now accepts a second `errorInfo` argument which may contain a componentStack. If an error originated from a component a stack will be included in the following cases. This change entails tracking the component hierarchy in prod builds now. While this isn't cost free it is implemented in a relatively lean manner. Deferring the most expensive work (reifying the stack) until we are actually in an error pathway. In the course of implementing this change a number of simplifications were made to the code which should make the stack tracking more resilient. We no longer use a module global to curry the stack up to some handler. This was delicate because you needed to always reset it properly. We now curry the stack on the task itself. Another change made was to track the component stack on SuspenseBoundary instances so that we can provide the stack when aborting suspense boundaries to help you determine which ones were affected by an abort.
… mode or during prerenders in prod mode (#27761) Historically React would produce component stacks for dev builds only. There is a cost to tracking component stacks and given the prod builds try to optimize runtime performance these stacks were left out. More recently React added production component stacks to Fiber in because it can be immensely helpful in tracking down hard to debug production issues. Fizz was not updated to have a similar behavior. With the advent of prerendering however stacks for production in Fizz are more relevant because prerendering is not really a dev-time task. If you want the ability to reason about errors or postpones that happen during a prerender having component stacks to interrogate is helpful and these component stacks need to be available in production otherwise you are really never going to see them. (it is possible that you could do dev-mode prerenders but we don't expect this to be a common dev mode workflow) To better support the prerender use case and to make error logging in Fizz more useful the following changes have been made 1. `onPostpone` now accepts a second `postponeInfo` argument which will contain a componentStack. Postpones always originate from a component render so the stack should be consistently available. The type however will indicate the stack is optional so we can remove them in the future if we decide the overhead is the wrong tradeoff in certain cases 2. `onError` now accepts a second `errorInfo` argument which may contain a componentStack. If an error originated from a component a stack will be included in the following cases. This change entails tracking the component hierarchy in prod builds now. While this isn't cost free it is implemented in a relatively lean manner. Deferring the most expensive work (reifying the stack) until we are actually in an error pathway. In the course of implementing this change a number of simplifications were made to the code which should make the stack tracking more resilient. We no longer use a module global to curry the stack up to some handler. This was delicate because you needed to always reset it properly. We now curry the stack on the task itself. Another change made was to track the component stack on SuspenseBoundary instances so that we can provide the stack when aborting suspense boundaries to help you determine which ones were affected by an abort. DiffTrain build for commit 63310df.
Historically React would produce component stacks for dev builds only. There is a cost to tracking component stacks and given the prod builds try to optimize runtime performance these stacks were left out. More recently React added production component stacks to Fiber in because it can be immensely helpful in tracking down hard to debug production issues. Fizz was not updated to have a similar behavior.
With the advent of prerendering however stacks for production in Fizz are more relevant because prerendering is not really a dev-time task. If you want the ability to reason about errors or postpones that happen during a prerender having component stacks to interrogate is helpful and these component stacks need to be available in production otherwise you are really never going to see them. (it is possible that you could do dev-mode prerenders but we don't expect this to be a common dev mode workflow)
To better support the prerender use case and to make error logging in Fizz more useful the following changes have been made
onPostpone
now accepts a secondpostponeInfo
argument which will contain a componentStack. Postpones always originate from a component render so the stack should be consistently available. The type however will indicate the stack is optional so we can remove them in the future if we decide the overhead is the wrong tradeoff in certain casesonError
now accepts a seconderrorInfo
argument which may contain a componentStack. If an error originated from a component a stack will be included in the following cases.This change entails tracking the component hierarchy in prod builds now. While this isn't cost free it is implemented in a relatively lean manner. Deferring the most expensive work (reifying the stack) until we are actually in an error pathway.
In the course of implementing this change a number of simplifications were made to the code which should make the stack tracking more resilient. We no longer use a module global to curry the stack up to some handler. This was delicate because you needed to always reset it properly. We now curry the stack on the task itself.
Another change made was to track the component stack on SuspenseBoundary instances so that we can provide the stack when aborting suspense boundaries to help you determine which ones were affected by an abort.