- 
          
 - 
                Notifications
    
You must be signed in to change notification settings  - Fork 1.2k
 
fix: async subscription do not run when mutate state synchronously after patch #2870
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: v3
Are you sure you want to change the base?
Conversation
          ✅ Deploy Preview for pinia-official canceled.
  | 
    
          ✅ Deploy Preview for pinia-playground ready!
 To edit notification comments on pull requests, go to your Netlify site configuration.  | 
    
| 
           Thanks! I will take a look when I can  | 
    
60b9c6d    to
    7844dba      
    Compare
  
    | 
           This PR is now based on  Does   | 
    
| 
           Thanks! I think it's fine to keep this for v3 only so don't worry about it  | 
    
          
commit:   | 
    
| () => { | ||
| shouldTrigger = isListening | ||
| }, | ||
| { deep: true, flush: 'sync' } | 
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'm revisitting this PR and realizing that this might impact perf too much
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 need to see in the playground the impact
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.
Without checking the playground, I think we can't go this way: #610
          
WalkthroughThe PR adds two subscription tests and refactors Pinia's store subscription logic: replaces multi-flag listening/locking with a single shouldTrigger flag, removes nextTick-based listening transitions, and splits a single watcher into two coordinated watchers for patch and direct-change handling. Changes
 Sequence Diagram(s)sequenceDiagram
    rect lightblue
    participant User
    participant Store
    participant WatcherFull as Watcher (full state)
    participant WatcherDirect as Watcher (direct/patch)
    participant Subscribers
    end
    User->>Store: perform patch / HMR payload
    Store->>Store: set isListening = false\nset shouldTrigger = true
    Store->>WatcherFull: emit (full-state watcher)
    WatcherFull->>Store: check shouldTrigger
    alt shouldTrigger true
        WatcherFull->>Subscribers: notify with patched events
    end
    Store->>Store: set isListening = true
    User->>Store: perform direct mutation
    Store->>Store: set isListening = false\nset shouldTrigger = true
    Store->>WatcherDirect: emit (direct-change watcher)
    WatcherDirect->>Store: check shouldTrigger
    alt shouldTrigger true
        WatcherDirect->>Subscribers: notify with direct-change events
    end
    Store->>Store: set isListening = true
    Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 
 Poem
 Pre-merge checks and finishing touches✅ Passed checks (2 passed)
 ✨ Finishing touches
 🧪 Generate unit tests (beta)
 📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
 🚧 Files skipped from review as they are similar to previous changes (1)
 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   | 
    
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.
Actionable comments posted: 0
🧹 Nitpick comments (2)
packages/pinia/__tests__/subscriptions.spec.ts (1)
357-357: Fix grammatical error in test description.The test name contains a grammatical error: "not be an array" should be "not an array".
Apply this diff:
- it('debuggerEvents is not be an array when subscription is not trigger by patch', () => { + it('debuggerEvents is not an array when subscription is not triggered by patch', () => {packages/pinia/src/store.ts (1)
267-268: Consider clarifying the shouldTrigger initialization.While the comment states "The initial value does not matter", initializing
shouldTriggertoundefinedmay cause confusion during debugging. Consider either:
- Initializing to
 falsefor clarity, or- Expanding the comment to explain that it's set synchronously before being checked by async watchers
 Apply this diff:
- let shouldTrigger: boolean | undefined // The initial value does not matter, and no need to set to true at the end + // Captures the listening state synchronously for async watchers; set by sync watcher before async watchers check it + let shouldTrigger: boolean | undefined
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/pinia/__tests__/subscriptions.spec.ts(1 hunks)packages/pinia/src/store.ts(7 hunks)
🔇 Additional comments (5)
packages/pinia/__tests__/subscriptions.spec.ts (1)
371-385: Good test coverage for the fix.This test effectively validates the core fix for issue #992, ensuring that subscriptions with all flush strategies ('sync', 'pre', 'post') are properly triggered when state is synchronously mutated after a patch operation.
packages/pinia/src/store.ts (4)
252-252: Condition refinement looks good.The added
!store._hotUpdatingcheck appropriately prevents debugger event accumulation during hot module replacement operations.
291-291: isListening state management is correct.The bookending of the patch operation with
isListening = false/trueproperly prevents automatic watcher firing during the patch, while manual triggering (lines 315-319) ensures subscribers are notified once.Also applies to: 313-313
624-624: HMR state transition is consistent.The synchronous
isListening = trueassignment aligns with the removal of nextTick-based state transitions and maintains consistency with the$patchimplementation.
437-467: Dual-watcher design appears intentional; performance concern is valid but unproven.Analysis reveals this is a deliberate implementation to handle timing correctly: during state mutations,
shouldTriggercaptures the currentisListeningstate synchronously viastop1, thenstop2fires the callback only if that captured state permits it. The design prevents subscriptions from firing at incorrect times due to race conditions with theisListeningflag.The performance overhead concern is valid—two watchers per subscription with deep watching on every mutation—but lacks empirical measurement. The codebase contains no performance benchmarks or complaints about subscription overhead. The trade-off appears accepted for correctness.
Before accepting or modifying this approach, measure actual performance impact in your usage patterns. If performance is acceptable, no changes needed. If degradation is significant, consider profiling to identify if this dual-watcher is the bottleneck or if other optimizations would suffice.
          Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@            Coverage Diff             @@
##               v3    #2870      +/-   ##
==========================================
- Coverage   91.01%   91.00%   -0.01%     
==========================================
  Files          17       17              
  Lines        1402     1401       -1     
  Branches      211      209       -2     
==========================================
- Hits         1276     1275       -1     
  Misses        125      125              
  Partials        1        1              ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
  | 
    
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
Improvements