-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
fix(query-core): query cancellation and reverting #9293
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
base: main
Are you sure you want to change the base?
Conversation
…perative methods to achieve that, we let the retryer's `onError` "transform" the result back to potentially TData, and if so, we'll resolve the promise to that instead; that means calls to `fetchQuery` will be "successful" when a cancellation happens in the meantime, but they will then resolve to the reverted data; also, if the initial fetch is cancelled, we would still throw, as there is no data to revert to.
View your CI Pipeline Execution ↗ for commit 9b710ab.
☁️ Nx Cloud last updated this comment at |
Sizes for commit 9b710ab:
|
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #9293 +/- ##
===========================================
+ Coverage 45.37% 59.64% +14.26%
===========================================
Files 207 136 -71
Lines 8277 5508 -2769
Branches 1872 1484 -388
===========================================
- Hits 3756 3285 -471
+ Misses 4080 1925 -2155
+ Partials 441 298 -143 🚀 New features to boost your workflow:
|
up until now, silent reverts have only stopped the query from going into error state, but the exposed promise was still rejected now, the promise won't reject because a silent revert indicates another refetch happening, so we'd want to get that promise's result instead this also means "silent" is an internal thing that shouldn't be used by consumers, because it can lead to never resolving promises instead
clientQueryClient.clear() | ||
serverQueryClient.clear() |
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.
@Ephem we now get another CancelledError
here if we clear the caches. They are local to this test, so clearing them shouldn’t be necessary, but I’m wondering where this is coming from in the first place 🤔
queryClient.fetchQuery({ | ||
queryClient.prefetchQuery({ |
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 now throws a CancelledError
because we call cancelQueries
with revert:false
, and since we don’t revert, the error makes it to the cache. It did so before too, but since the refactor to async/await, it shows up as unhandled in node.
expect(count).toBe(4) | ||
// 1 original fetch + 2 retries | ||
expect(count).toBe(3) |
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 thought hard about this but it was wrong before 😂
we don't want errors that get reverted internally to throw on imperative methods. to achieve that, I've:
retryer
away fromonSuccess
/onError
callbacks, because we can just use promise handling (then/catch) where the retryer is created. We do the same with mutations so this streamlines things, too.’revert’
back to the previous state. That means a revert will not trigger imperative methods to fail the promise, but it will resolve them with the data we revert to internally.fetch
again but wants tocancelRefetch: true
, which is whatinvalidateQueries
does per default. In those cases, the original promise would also reject, but we would actually like it to receive the value from the second fetch. This is now done (see the added test case), but it implies that if we cancel withsilent:true
, we need another fetch to happen. Internally, this is guaranteed, butsilent:boolean
is also exposed onqueryClient.cancelQueries
, so if you do that without triggering another fetch, the promise will be pending until a new fetch happens somehow.Generally, the exposed promise was never transformed when we had a CancelationError, which is why users kept seeing it (e.g. in route loaders)
CancelledError
on imperative methods. This wasn’t an issue when just usinguseQuery
, because the internal Query State was mapped correctly.