Make mergeCollectionWithPatches cache-first [No QA]#91160
Draft
elirangoshen wants to merge 2 commits into
Draft
Conversation
…ollectionWithPatches Bumps react-native-onyx to the head of Expensify/react-native-onyx#787, which makes mergeCollectionWithPatches cache-first (matches every other Onyx write method): cache + subscribers are updated synchronously before the storage write is issued, so subscribers still reflect the merged data when the storage write fails (e.g. IDB corruption). Fixes Expensify#90634
Contributor
|
|
41 tasks
The patch reverted Onyx PR Expensify#770 locally. Onyx PR Expensify#785 merged the canonical upstream revert, and the SHA bump in this PR (42b796d64f92b8122c1e545adb6aac587df7a787) is branched off Onyx main after Expensify#785 -- so the lines the patch tries to delete are already gone, causing patch-package to fail on apply. Deletes the patch file and clears its details.md entry. Behavior is unchanged: the same revert is now in upstream Onyx. Related: Expensify#86181
Draft
51 tasks
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.
Explanation of Change
Bumps
react-native-onyxto the head of Expensify/react-native-onyx#787, which makesmergeCollectionWithPatchescache-first (matching every other Onyx write method —setWithRetry,applyMerge,setCollectionWithRetry,partialSetCollection,clear).Before:
mergeCollectionWithPatchespushedStorage.multiMerge/Storage.multiSetfirst and bundled thecache.merge+keysChangedupdate into apreviousCollectionPromise.then(...)chain that ran in parallel with storage. If the cache update lost the race to a storage failure, subscribers could observe a state inconsistent with the merge that was logically applied — the only Onyx write path where a storage error could miss the cache.After:
get()first pre-warms the cache from storage for cache-miss existing keys (sync no-op for cache hits), thencache.merge+keysChangedrun synchronously before the storage promises are issued. Subscribers receive the merged data before any storage write is attempted; storage failure can no longer race the cache update.This is the only diff in this PR — just the
package.jsonandpackage-lock.jsonSHA bump (5 lines total). The actual code change lives in the linked Onyx PR.Fixed Issues
$ #90634
PROPOSAL:
Tests
These steps exercise
mergeCollectionWithPatchesend-to-end across the App's most common callers (Search,IOU/Hold,Report/MarkAllMessageAsRead,Report/index,Policy/Policy,MoneyRequest), plus everyOnyx.updatebatch that includes aMERGE_COLLECTIONop (LHN refresh, Pusher events, etc.).Setup
npm installunder Node 20.20.0 (the repo's pinned version) to fetch the new Onyx SHAnpm run weband open https://dev.new.expensify.com:8082/ in Chrome with DevTools open1. Initial hydration — the post-auth Pusher payload and
OpenAppresponse trigger a large batchedOnyx.updatethat includesMERGE_COLLECTIONops for reports, policies, and personal details.main2. Send a chat message — optimistic merge into the report's
reportActions_collection.3. Mark all as read (
Report/MarkAllMessageAsRead.tsx→MERGE_COLLECTION)4. Search & filter (
Search.ts→MERGE_COLLECTIONfor snapshot results)5. Hold / unhold an expense (
IOU/Hold.ts→MERGE_COLLECTION)6. Create a money request (
MoneyRequest.ts→ directOnyx.mergeCollectioncall)7. Switch workspaces (
Policy/Policy.ts→MERGE_COLLECTION)8. Storage-failure simulation — the core regression guard for this fix
The fix protects against a race where a failing
Storage.multiMergecould leave the cache without the merge's update (and therefore leave subscribers stale). With the fix in place,cache.mergeandkeysChangedalways fire before the storage write, so subscribers stay correct regardless of storage outcome.OnyxDB— only one or two databases listed)mergeCollection-driven action — switch workspaces, send a chat message, or apply a search filterEvidence
A short screen recording of tests 1–4 (functional smoke), 8 (IDB-failure simulation), and 9 (cold-cache) is sufficient evidence. The race-condition tests (8, 9) are the ones reviewers should focus on since the rest are general
mergeCollectionsmoke that would surface in any e2e run.Offline tests
The fix improves offline / storage-failure resilience — but it is not directly testable from the offline simulator since the race manifests only when storage operations themselves fail (e.g. IDB corruption / quota errors). The Onyx-side automated test mocks
Storage.multiMergerejection to cover this path.For best-effort offline verification:
QA Steps
[No QA] — this fix targets a race condition that surfaces only when underlying storage (
Storage.multiMerge) fails. The failure mode (IDB corruption / non-retriable IndexedDB errors) is not reproducible in staging through manual interaction, so there is no meaningful manual QA path. Verification is via automated tests in the companion Onyx PR.PR Author Checklist
Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari
Screen.Recording.2026-05-20.at.14.53.27.mov
Screen.Recording.2026-05-20.at.14.54.02.mov
Screen.Recording.2026-05-20.at.15.00.50.mov
Screen.Recording.2026-05-20.at.15.06.56.mov
Screen.Recording.2026-05-20.at.15.10.33.mov
Screen.Recording.2026-05-20.at.15.12.19.mov
Screen.Recording.2026-05-20.at.15.14.31.mov
Screen.Recording.2026-05-20.at.15.16.13.mov
Screen.Recording.2026-05-20.at.15.16.36.mov
Screen.Recording.2026-05-20.at.15.17.07.mov
Screen.Recording.2026-05-20.at.15.19.23.mov
Screen.Recording.2026-05-20.at.15.27.05.mov