Repository navigation
fix(query-core): unpause a mutation that can start after its onMutate - #11788
maxymlyskov wants to merge 1 commit into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (2)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthroughAfter ChangesMutation pause state
Priority: ➖ Normal Estimated code review effort: 2 (Simple) | ~10 minutes Change: Bug fix Suggested reviewers: Merge Risk: ⚪ Minimal · up to No merge-blocking issue is identified. Run the mutation tests as a normal pre-merge check. Security Architecture ReviewSecurity architecture risk: ⚪ Minimal · up to The fix aligns pause state with execution eligibility, reducing unintended mutation replay without bypassing scoped ordering or network-mode controls. No material security risk was identified in the reviewed transition. Retained concerns Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
Resilience and Maintainability Implications
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Mutation.execute() computes isPaused before it awaits the MutationCache and mutation onMutate callbacks, and dispatches pending with that value. When the mutation cannot start at that point (queued behind another mutation in its scope, or offline) but can by the time onMutate resolves, retryer.start() runs it directly instead of going through pause(), so no continue action is dispatched and the mutation reports isPaused: true for its whole request. defaultShouldDehydrateMutation and resumePausedMutations read that flag, so the in-flight mutation is dehydrated as not yet sent and runs a second time after a restore. Dispatch continue right before start() when the mutation is still marked paused but can start now, as TanStack#9015 does for a restored mutation. Source: packages/query-core/src/mutation.ts execute(), packages/query-core/src/retryer.ts start(), hydration.ts defaultShouldDehydrateMutation.
934a88c to
78b4a20
Compare
🦋 Changeset detectedLatest commit: 78b4a20 The changes in this PR will be included in the next version bump. This PR includes changesets to release 24 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
🎯 Changes
A mutation that can't start when
mutate()is called (queued behind another mutation in the same scope, or offline) starts aspendingwithisPaused: true. If it becomes able to start while itsonMutateis still running,retryer.start()runs themutationFnstraight away, but nothing dispatchescontinue, soisPausedstaystruefor the whole request.defaultShouldDehydrateMutationreads that flag, so the in-flight mutation gets dehydrated, and after a restoreresumePausedMutations()sends it again. Dehydrating while the second of two scoped mutations is in flight, then hydrating into a fresh client and resuming, main sendsa, b, band this branch sendsa, b. That is the case #6238 calls unwanted: an in-flight mutation shouldn't be persisted and replayed.The fix dispatches
continueright beforeretryer.start()when the mutation is still marked paused but can start now, the same dispatch #9015 added for restored mutations. It readsthis.state.isPaused, so a restored mutation that was already continued doesn't get a second one.Tests in
mutation.test.tsx:onMutateruns is unpaused once it starts. On main it staysisPaused: true.onMutateresolves before its turn stays paused. It passes on main and fails if the fix unpauses too early.✅ Checklist
pnpm run test:pr, or these tests do not apply to this pull request.🚀 Release Impact
Summary by CodeRabbit
onMutateis still resolving now resume correctly. Mutations waiting for connectivity can proceed once they are able to run, while mutations queued behind another mutation remain paused until the preceding mutation finishes. This keeps mutation execution aligned with availability and queue order.