fix: async subscription do not run when mutate state synchronously after patch
Fix #992
I run into same issue when I porting Pinia to Vue Mini, and I found another debugger issue for sync subscription (please see the test).
I managed a way to fix them, the fix may have extra memory / performance costs, I didn't do any measurements. But I used it in Pinia for Vue Mini anyway.
Summary by CodeRabbit
-
Tests
- Updated expectations for subscription synchronization and added a test covering debugger events when a subscription is not triggered by a patch.
-
Improvements
- More consistent subscription triggering so events are delivered reliably after patches and direct synchronous mutations.
Deploy Preview for pinia-official canceled.
| Name | Link |
|---|---|
| Latest commit | 807cfd645dce7fe788ad087d8f138982712e15ad |
| Latest deploy log | https://app.netlify.com/projects/pinia-official/deploys/6913650e50eb9100086cd252 |
Deploy Preview for pinia-playground ready!
| Name | Link |
|---|---|
| Latest commit | 60b9c6dee4c007b747e02bca37444744620a9930 |
| Latest deploy log | https://app.netlify.com/sites/pinia-playground/deploys/677912aa1a67c50008c55c37 |
| Deploy Preview | https://deploy-preview-2870--pinia-playground.netlify.app |
| Preview on mobile | Toggle QR Code...Use your smartphone camera to open QR code link. |
To edit notification comments on pull requests, go to your Netlify site configuration.
Thanks! I will take a look when I can
This PR is now based on v3 and can be merged
Does v2 need this fix? If so, I can open another PR to fix v2
Thanks! I think it's fine to keep this for v3 only so don't worry about it
npm i https://pkg.pr.new/@pinia/nuxt@2870
npm i https://pkg.pr.new/pinia@2870
npm i https://pkg.pr.new/@pinia/testing@2870
commit: 807cfd6
Walkthrough
Refactors store subscription internals to replace multi-flag listening with a single gating flag and two coordinated watchers, and updates tests (adjusted expectations and one new test) reflecting changed sync flush behavior and debugger event shape for non-patch subscriptions.
Changes
| Cohort / File(s) | Summary |
|---|---|
Tests packages/pinia/__tests__/subscriptions.spec.ts |
Adjusted expectations for sync flush behavior (pre/post spies now expected to be called twice when a direct mutation follows a patch) and added one test asserting debuggerEvents is not an array when a subscription with flush: 'sync' is triggered by a direct mutation (non-patch). |
Subscription Logic packages/pinia/src/store.ts |
Replaced multi-flag listening/nextTick transitions with a single shouldTrigger gate and direct isListening toggles; split a single watcher into two scoped watchers (stop1, stop2) to separate full-state vs direct/patch change detection; removed activeListener and nextTick-based throttling; updated HMR and patch dispatch to use the new gating and cleanup. |
Sequence Diagram(s)
sequenceDiagram
rect rgba(173,216,230,0.15)
participant User
participant Store
participant WatcherFull as Watcher (full state)
participant WatcherDirect as Watcher (direct/patch)
participant Subscribers
end
User->>Store: apply patch / HMR payload
Store->>Store: isListening = false\nshouldTrigger = true
Store->>WatcherFull: full-state watcher fires
WatcherFull->>Store: checks shouldTrigger
alt shouldTrigger true
WatcherFull->>Subscribers: notify (patch events)
end
Store->>Store: isListening = true
User->>Store: perform direct mutation
Store->>Store: isListening = false\nshouldTrigger = true
Store->>WatcherDirect: direct-change watcher fires
WatcherDirect->>Store: checks shouldTrigger
alt shouldTrigger true
WatcherDirect->>Subscribers: notify (direct-change event)
end
Store->>Store: isListening = true
Estimated code review effort
π― 3 (Moderate) | β±οΈ ~25 minutes
- Review coordination between the two watchers and combined cleanup logic.
- Verify HMR and patch paths correctly set/clear
shouldTriggerandisListening. - Check the updated tests for correct expectations around sync flush ordering and debugger event shapes.
Poem
π° I hopped through lines of store and test,
Split watchers tending state's unrest.
One gate decides if listeners sing,
Patch then mutates β the bells now ring.
π₯β¨
Pre-merge checks and finishing touches
β Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | β οΈ Warning | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
β Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | β Passed | Check skipped - CodeRabbitβs high-level summary is enabled. |
| Title check | β Passed | The title directly addresses the core fix: preventing async subscriptions from running when state is mutated synchronously after a patch, which is precisely what the changeset implements. |
β¨ Finishing touches
- [ ] π Generate docstrings
π§ͺ Generate unit tests (beta)
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
π Recent review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π₯ Commits
Reviewing files that changed from the base of the PR and between bebea166d4b67aad3e93eae4017d29d14de67d9e and 807cfd645dce7fe788ad087d8f138982712e15ad.
π Files selected for processing (2)
-
packages/pinia/__tests__/subscriptions.spec.ts(1 hunks) -
packages/pinia/src/store.ts(7 hunks)
π Additional comments (7)
packages/pinia/__tests__/subscriptions.spec.ts (2)
372-374: LGTM - Test expectations correctly reflect the fix.The updated expectations verify that pre and post flush subscriptions now correctly fire when state is synchronously mutated after a patch. The first call is from the patch (Line 367), and the second call is from the direct mutation (Line 368), which is the behavior this PR fixes.
376-388: LGTM - Good addition to verify debugger event shape.This test correctly verifies that
eventsis a singleDebuggerEventobject (not an array) for direct mutations, distinguishing it from patch operations whereeventsis an array.packages/pinia/src/store.ts (5)
252-252: LGTM - Condition refinement prevents debugger event errors.The updated condition correctly distinguishes between active listening (line 249) and patch operations where events should be collected in an array. The
!store._hotUpdatingguard prevents errors during HMR.
267-268: Variable initialization is correct.The
undefinedinitialization for both flags is appropriate.isListeningis set totrueat line 752, andshouldTriggeris always set by the sync watcher before being checked.
291-313: Patch gating mechanism is correct.Setting
isListening = falsebefore mutations andtrueafter correctly gates the subscription watchers, preventing double-firing while allowing the manualtriggerSubscriptionscall to work.
624-624: LGTM - Simplified HMR flow.Removing the
nextTickwrapper and settingisListening = truedirectly is consistent with the new synchronous gating approach and simplifies the HMR update flow.
437-467: Performance verification and documentation needed for two-watcher implementation.The review comment's concerns are validated by issue #610, which documents that the
deep: true+flush: 'sync'watcher combination creates measurable performance overhead. The two-watcher approach creates this tradeoff between correctness (fixing subscription behavior) and performance (increased watcher count per subscription).The suggestions to measure real-world impact, document implications, and evaluate alternatives are appropriate given the known performance concerns in the project history. Proceed with the recommended verification steps.
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 @coderabbitai help to get the list of available commands and usage tips.
Codecov Report
:white_check_mark: All modified and coverable lines are covered by tests.
:white_check_mark: Project coverage is 90.90%. Comparing base (9182fdb) to head (807cfd6).
Additional details and impacted files
@@ Coverage Diff @@
## v3 #2870 +/- ##
==========================================
- Coverage 90.90% 90.90% -0.01%
==========================================
Files 19 19
Lines 1683 1682 -1
Branches 237 235 -2
==========================================
- Hits 1530 1529 -1
Misses 151 151
Partials 2 2
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
:rocket: New features to boost your workflow:
- :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
- :package: JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.