Skip to content

Firestore: preserve batch IDs after queue restart - #16751

Open
dajiaohuang wants to merge 7 commits into
firebase:mainfrom
dajiaohuang:fix/firestore-global-index-batch-id
Open

dajiaohuang wants to merge 7 commits into
firebase:mainfrom
dajiaohuang:fix/firestore-global-index-batch-id

Conversation

@dajiaohuang

@dajiaohuang dajiaohuang commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Restore Firestore's global mutation batch ID high-water mark from persisted index offsets for every user when a LevelDB mutation queue starts. Acknowledging all mutations and reopening an indexed store must not cause the next pending write to reuse a batch ID that the persisted index offset already excludes.

The change keeps the queue fix focused: the additional mutation-table and write-time overflow assertions are removed, and an unexpectedly large persisted index offset is clamped before the new increment rather than asserting during startup. The CHANGELOG entry is under Unreleased and references #16751.

Fixes #16742

Validation

  • StartIncludesLargestIndexedBatchIdAcrossUsers verifies the greatest persisted offset across two users is included when restarting the queue. Index configuration is global; each user has its own persisted index state.
  • LevelDbLocalStoreRestartTest.IndexedCacheQueryIncludesNewPendingWrite uses actual LevelDB persistence and LocalStore: configure an index, write/backfill, acknowledge, finish backfilling the acknowledged document, close/reopen the same database, write another matching document, and verify both documents through an indexed cache query without a collection scan.
  • clang-format 23.1.2 exact-file validation and git diff --check pass.
  • Actual Linux CMake Debug validation compiled firestore_local_test from production sources: all 790 tests passed. With only the global index high-water restoration disabled, both StartIncludesLargestIndexedBatchIdAcrossUsers and IndexedCacheQueryIncludesNewPendingWrite failed; restoring the patch made both pass in the complete local-test target. Run: https://github.com/dajiaohuang/firebase-ios-sdk/actions/runs/37100308605/job/111138367633 . The validated Firestore source/test tree is identical to c87ebae; the validation workflow stays on a separate fork branch. This is Linux local-store validation, not an iOS SDK or backend integration run.

Current-main integration

Merged current main c276eca normally at 5aba540 to resolve the Firestore CHANGELOG conflict. Both Unreleased entries are retained. All six affected Firestore production/test files are byte-identical to the previously validated c87ebae tree; git diff --check passes. The earlier 790-test Linux result remains component evidence, not a new post-merge run. No Apple tests were run on this Windows host; updated-head CI is separate.

@gemini-code-assist

Copy link
Copy Markdown
Contributor
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the 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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. 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.

@paulb777

paulb777 commented Oct 1, 2026

Copy link
Copy Markdown
Member

/gemini review

@paulb777

paulb777 commented Oct 1, 2026

Copy link
Copy Markdown
Member

The reviewing agent claims this is also an issue on Android

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request ensures that pending document overlays for indexed queries are preserved when the mutation queue is recreated. It achieves this by updating LevelDbMutationQueue::Start() to restore the next batch ID using the global high-water mark of indexed batch IDs across all users, retrieved via a new GetHighestBatchIdAcrossUsers() method in LevelDbIndexManager. Additionally, assertions were added to prevent batch ID overflow, and a unit test was introduced to verify the new behavior. Feedback is provided to check the LevelDB iterator's status after the loop in GetHighestBatchIdAcrossUsers() to prevent potential silent data corruption from unhandled read errors.

Comment thread Firestore/core/src/local/leveldb_index_manager.cc
@paulb777

paulb777 commented Oct 1, 2026

Copy link
Copy Markdown
Member

Thanks for the contribution! A few housekeeping items:

  • CHANGELOG: The entry in Firestore/CHANGELOG.md is under # 13.0.0, which has already shipped. Please move it under a # Unreleased heading at the top of the file (add the heading if it isn't there yet).
  • PR number: end the entry with this PR's number, e.g. - [fixed] Description. (#16751).
  • Formatting: after updating, please run scripts/style.sh so the infra.check job passes. It needs clang-format 23 and swiftformat (see scripts/setup_check.sh). If you can't run it locally, the infra.check log lists the files that need formatting.

@paulb777 paulb777 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tracking this down. The analysis matches what I see in the code: index offsets are persisted per user and outlive the mutation queue, so restarting with an empty queue can hand out batch IDs at or below an existing largest_batch_id, and those pending writes are then skipped by index-backed cache queries (#16742). Taking the max across users in Start() looks like the right fix. This will also need a review from the Firestore team.

A couple of requests inline.

Comment thread Firestore/core/src/local/leveldb_mutation_queue.cc Outdated
Comment thread Firestore/core/test/unit/local/leveldb_mutation_queue_test.cc Outdated
@dajiaohuang

Copy link
Copy Markdown
Contributor Author

Addressed the requested review changes: moved the CHANGELOG entry to Unreleased with #16751, removed the two unrelated mutation-table/write-time overflow assertions, and replaced the new persisted-index assertion with a clamp before incrementing.

Renamed the queue test to StartIncludesLargestIndexedBatchIdAcrossUsers, retaining the existing two-user fixture with global index configuration and separate user offsets. Added a LocalStore-level regression using a real LevelDB close/reopen and indexed cache query after acknowledging all writes. It verifies that the new pending document is returned without a collection scan.

Actual Linux CMake Debug validation compiled firestore_local_test from production sources: all 790 tests passed. With only the global index high-water restoration disabled, both StartIncludesLargestIndexedBatchIdAcrossUsers and IndexedCacheQueryIncludesNewPendingWrite failed; restoring the patch made both pass in the complete local-test target. Run: https://github.com/dajiaohuang/firebase-ios-sdk/actions/runs/37100308605/job/111138367633 . The validated Firestore source/test tree is identical to c87ebae; the validation workflow stays on a separate fork branch. This is Linux local-store validation, not an iOS SDK or backend integration run.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Firestore cache index misses pending writes after a relaunch

2 participants