Skip to content

Conversation

@yangmingshan
Copy link

@yangmingshan yangmingshan commented Jan 4, 2025

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

    • Added coverage for subscription behavior when state is mutated directly and when multiple flush strategies are used.
  • Improvements

    • Improved subscription reliability and triggering semantics so subscribers receive events consistently after patches and synchronous mutations.

@netlify
Copy link

netlify bot commented Jan 4, 2025

Deploy Preview for pinia-official canceled.

Name Link
🔨 Latest commit bebea16
🔍 Latest deploy log https://app.netlify.com/projects/pinia-official/deploys/6908822f310c790008edbba3

@netlify
Copy link

netlify bot commented Jan 4, 2025

Deploy Preview for pinia-playground ready!

Name Link
🔨 Latest commit 60b9c6d
🔍 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.

Copy link
Member

posva commented Jan 4, 2025

Thanks! I will take a look when I can

@posva posva changed the base branch from v2 to v3 February 12, 2025 08:51
@yangmingshan
Copy link
Author

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

@posva
Copy link
Member

posva commented Feb 26, 2025

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

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jul 23, 2025

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: 92a5c0f

() => {
shouldTrigger = isListening
},
{ deep: true, flush: 'sync' }
Copy link
Member

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

Copy link
Member

@posva posva Nov 3, 2025

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

Copy link
Member

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

@coderabbitai
Copy link

coderabbitai bot commented Nov 3, 2025

Walkthrough

The 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

Cohort / File(s) Summary
Test Coverage
packages/pinia/__tests__/subscriptions.spec.ts
Added two tests: one asserting debuggerEvents is not an array for direct (non-patch) mutations with flush: 'sync'; another asserting subscriptions fire correctly when a synchronous direct mutation follows a patch for sync, pre, and post flush strategies.
Subscription Logic
packages/pinia/src/store.ts
Replaced guarded multi-flag listening logic with a single shouldTrigger flag; removed nextTick-based transitions; split a single watcher into two scoped watchers (stop1, stop2) with combined cleanup; updated HMR payload handling and debug/patch event dispatch to respect shouldTrigger.

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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Pay attention to watcher coordination and combined cleanup in store.ts.
  • Verify HMR flow and immediate isListening transitions behave identically to former deferred behavior.
  • Validate the new tests accurately exercise flush strategies and debugger event shapes.

Poem

🐰 I hopped through state and code so spry,
Two watchers watch as patches fly.
No locks, no waits, just shouldTrigger’s cheer,
Subscriptions wake — both far and near.
🍃✨

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'fix: async subscription do not run when mutate state synchronously after patch' directly describes the main fix in the changeset. The changes to store.ts implement a fix for async subscriptions not running when the state is mutated synchronously after a patch, which is exactly what the title conveys. The title is specific, clear, and accurately reflects the primary objective of the PR as confirmed by the PR objectives and code changes.
✨ 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 92a5c0f and bebea16.

📒 Files selected for processing (1)
  • packages/pinia/__tests__/subscriptions.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/pinia/tests/subscriptions.spec.ts

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.

Copy link

@coderabbitai coderabbitai bot left a 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 shouldTrigger to undefined may cause confusion during debugging. Consider either:

  1. Initializing to false for clarity, or
  2. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 1ea0440 and 92a5c0f.

📒 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._hotUpdating check 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/true properly 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 = true assignment aligns with the removal of nextTick-based state transitions and maintains consistency with the $patch implementation.


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, shouldTrigger captures the current isListening state synchronously via stop1, then stop2 fires the callback only if that captured state permits it. The design prevents subscriptions from firing at incorrect times due to race conditions with the isListening flag.

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
Copy link

codecov bot commented Nov 3, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.00%. Comparing base (1ea0440) to head (92a5c0f).

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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

posva added a commit that referenced this pull request Nov 3, 2025
Extracted from #2870
to fix #992
but can't be merged because the sync flush introduced a perf regression
@posva posva moved this from 🧑‍💻 In progress to 💬 In discussion in Pinia Roadmap Nov 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 💬 In discussion

Development

Successfully merging this pull request may close these issues.

$subscribe miss mutations with type of direct immediately afterpatch mutations

2 participants