pinia icon indicating copy to clipboard operation
pinia copied to clipboard

fix: async subscription do not run when mutate state synchronously after patch

Open yangmingshan opened this issue 1 year ago β€’ 8 comments

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.

yangmingshan avatar Jan 04 '25 10:01 yangmingshan

Deploy Preview for pinia-official canceled.

Name Link
Latest commit 807cfd645dce7fe788ad087d8f138982712e15ad
Latest deploy log https://app.netlify.com/projects/pinia-official/deploys/6913650e50eb9100086cd252

netlify[bot] avatar Jan 04 '25 10:01 netlify[bot]

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...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

netlify[bot] avatar Jan 04 '25 10:01 netlify[bot]

Thanks! I will take a look when I can

posva avatar Jan 04 '25 12:01 posva

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

yangmingshan avatar Feb 26 '25 14:02 yangmingshan

Thanks! I think it's fine to keep this for v3 only so don't worry about it

posva avatar Feb 26 '25 14:02 posva

Open in StackBlitz

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

pkg-pr-new[bot] avatar Jul 23 '25 16:07 pkg-pr-new[bot]

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 shouldTrigger and isListening.
  • 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 events is a single DebuggerEvent object (not an array) for direct mutations, distinguishing it from patch operations where events is 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._hotUpdating guard prevents errors during HMR.


267-268: Variable initialization is correct.

The undefined initialization for both flags is appropriate. isListening is set to true at line 752, and shouldTrigger is always set by the sync watcher before being checked.


291-313: Patch gating mechanism is correct.

Setting isListening = false before mutations and true after correctly gates the subscription watchers, preventing double-firing while allowing the manual triggerSubscriptions call to work.


624-624: LGTM - Simplified HMR flow.

Removing the nextTick wrapper and setting isListening = true directly 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.

❀️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot] avatar Nov 03 '25 10:11 coderabbitai[bot]

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.

codecov[bot] avatar Nov 03 '25 10:11 codecov[bot]