fix(firestore): record pending target requests at stream-start time#9762
fix(firestore): record pending target requests at stream-start time#9762ShaneBreazeale wants to merge 4 commits intofirebase:mainfrom
Conversation
…arget responses React 19 StrictMode double-invokes effects, causing listen/unlisten/re-listen sequences that can result in more server acks than pending requests. The previous hardAssert permanently poisoned the Firestore SDK instance. Now we clamp pendingResponses to 0 and log a warning instead. Fixes firebase#9729
🦋 Changeset detectedLatest commit: e63809b The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses a critical race condition in Firestore's watch stream management, particularly affecting environments like React 19 StrictMode. By shifting the timing of pending target request bookkeeping, it prevents inconsistencies that could arise from rapid listen/unlisten cycles during stream startup or reconnection. This ensures the SDK remains stable and functional even under aggressive component lifecycle behaviors, significantly improving resilience and preventing application crashes. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request resolves an issue (#9729) where the Firestore SDK could crash in environments like React StrictMode due to an incorrect count of pending watch stream responses. The changes introduce a mechanism to pre-record pending target requests during stream startup and modify how target responses are handled, preventing negative pendingResponses from causing a fatal assertion. A new sendWatchMessage function is added, and onWatchStreamOpen is updated to manage target states more robustly. Comprehensive unit and integration tests have been added to validate these changes. There is an opportunity to refactor sendWatchMessage and sendWatchRequest to extract common logic and improve maintainability.
Move the pendingResponses bookkeeping for restored targets from onWatchStreamOpen into startWatchStream, closing the timing window where a rapid unlisten/listen cycle (e.g. React 19 StrictMode double-invoking effects) could fire before the stream opens and leave the counter in an inconsistent state. Adds 4 spec tests covering: - unlisten/listen after stream reconnect - multiple unlisten/listen cycles after reconnect - unlisten during stream startup before open - multi-target reconnect with one target cycling Fixes firebase#9729
af4b7a4 to
46eb63e
Compare
d19066e to
e63809b
Compare
|
Hi @ShaneBreazeale, Thank you for putting this PR together and for proposing a solution to the pendingResponses < 0 assertion. I appreciate the effort to address this issue. I spent some time reviewing the fix, which suggests that rapid listen and unlisten calls before onWatchStreamOpen fires are the root cause of the negative pendingResponses count. However, in the main branch, recordPendingTargetRequest is only called inside sendWatchRequest (and sendUnwatchRequest), which are only invoked after the watch stream is successfully opened. If a target is listened to and then unlistened to before the stream opens, it is removed from the internal listenTargets map. No network requests are sent, and no pendingResponses increments/decrements occur. When the stream eventually opens, it only processes the targets remaining in the map at that moment. Therefore, this specific sequence cannot result in a negative counter in the current implementation. If I'm misunderstanding your fix or your assessment of the problem, please let me know. That said, we were able to get a reproduction of the ca9 assertion caused by React strict mode's double render. Fix is in progress #9842 |
Move the pendingResponses bookkeeping for restored targets from
onWatchStreamOpen into startWatchStream, closing the timing window
where a rapid unlisten/listen cycle (e.g. React 19 StrictMode
double-invoking effects) could fire before the stream opens and
leave the counter in an inconsistent state.
Adds 4 spec tests covering:
Fixes #9729