Repository navigation
[HSF-476] fix: batch snapshot notifications to stop React #185 on write bursts - #52
Merged
Merged
Conversation
…te bursts Co-Authored-By: Andrew Krippner <andrewkrip@gmail.com>
Contributor
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
Original prompt from Andrew
|
Contributor
There was a problem hiding this comment.
✅ Devin resolved all 2 findings on 7a58a0a
Fixed by Devin (2)
- One subscriber blocks unrelated notifications
- Local updates trigger duplicate subscriber callbacks
Co-Authored-By: Andrew Krippner <andrewkrip@gmail.com>
The batcher was per subscription, so a burst that lands as one snapshot on each of many document subscriptions still notified every one synchronously and still threw React #185. One batcher per store now folds them into one trailing notify. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwc3zMrUoB5ykW8WFppENr
The shared trailing notify ran every deferred subscription in one loop, so a throw from one subscriber (React #185 is thrown from inside the callback) skipped the rest and left their consumers stale. Each one now runs, and the first error is rethrown afterwards. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwc3zMrUoB5ykW8WFppENr
If the first snapshot's notify threw (React #185 is thrown from inside the subscriber), the window never opened and every later snapshot in the burst notified synchronously, the exact pattern the batcher exists to stop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwc3zMrUoB5ykW8WFppENr
andrewkrippner
added a commit
that referenced
this pull request
Oct 29, 2026
Bump @hvakr/firestate from 0.2.0 to 0.2.1 to release #52. Co-authored-by: Cody (HVAKR bot) <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
onSpaceWritewrites to 100+ spaces one at a time. Firestore then delivers that many snapshots in one task, and each one callednotify(). That gave one synchronoususeSyncExternalStorecommit per snapshot, and React 19 threw #185 (Maximum update depth) once 50+ commits ran back to back while an effect kept scheduling work. This is the HSF-476 stack:handleSnapshot → notify → forceStoreRerender.createSnapshotBatcher(src/core/snapshot-batcher.ts).notify()is split intopublishState()(updatesgetState()/getHandle()at once) andnotifySubscribers(). For snapshots, the first one in a task notifies synchronously, and the rest of that task's snapshots fold into onesetTimeout(0)notify. One batcher is shared by every subscription on a store, so a burst spread across many document subscriptions (one snapshot each) folds the same way as a burst on one collection.stop()drops the subscription's pending notify.update/set/add/remove/sync) still notify synchronously, and a single snapshot behaves as before.Risk: low. At most one task of delay before subscribers see the end of a burst. State reads always return the latest data.
How to verify
pnpm typecheck && pnpm test. The newsrc/__tests__/snapshot-batching.test.tsfails on master: a burst of 100 snapshots, sent sync or through a microtask chain, notifies twice instead of 100 times, on one subscription or spread across 100 document subscriptions.createRoot, in hvakr). 60 rapid notifies plus a consumeruseEffect(() => setState(value), [value])throw #185 at notify 52. Routed throughcreateSnapshotBatcher, there is no error and the final value renders. That was a scratch test and is not committed.@hvakr/firestateinpackages/app.Link to Devin session: https://app.devin.ai/sessions/6640d72ea84e43d4aa4b46cf0c1f16bd
Open in Devin Desktop: https://app.devin.ai/desktop/session/6640d72ea84e43d4aa4b46cf0c1f16bd?variant=devin
Requested by: @andrewkrippner
<source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v3.svg"><img alt="View with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png" width="162" height="28">https://backend.blacksmith.sh/track/enable-autofix?expires=1795792068&installation_model_id=6841&pr_number=52&ref=codesmith_pr_footer&repository=flowcircuits%2Ffirestate&return_to=https%3A%2F%2Fgithub.com%2Fflowcircuits%2Ffirestate%2Fpull%2F52&signature=1a9993cf938b5931ec41570695985694c7d14c00ee8a0e40e06211ea2c41e217"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img alt="Autofix with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png" width="190" height="28">Need help on this PR? Tag
@codesmith-botwith what you need.<source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-devin-review-dark.svg?v=4"><img src="https://static.devin.ai/assets/gh-devin-review-light.svg?v=4" alt="Devin Review">