fix(query-core): unpause a mutation that can start after its onMutate - #11788
maxymlyskov wants to merge 1 commit into
Conversation
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.
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: TanStack/query/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (3)
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 The change clears stale pause state when a mutation becomes eligible to run, while preserving required queue pauses. No actionable merge-blocking risk remains based on the supplied evidence. 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 |
🎯 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. This covers mutations waiting for connectivity and preserves the paused state of mutations queued behind another mutation.