Try to synchronize operations within one update#490
Conversation
|
CLA Assistant Lite bot All contributors have signed the CLA ✍️ ✅ |
|
@tgolen I think I found a way to fix the issue of the out-of-order updates. The main idea is the following: when an This PR is super draft, it lacks the validations as well as the broadcasting of the updates, but I would appreciate your opinion on the idea as a whole. |
tgolen
left a comment
There was a problem hiding this comment.
I love the direction this is going in and I think your approach is a good one. I hope it holds up as you keep working on it!
|
In general, it does work – I gave it a little test run. Calls like these: Lines 1511 to 1516 in 7cbf836 Lines 1153 to 1154 in 7cbf836 And all the places where we call the What broadcasting approach would you suggest using here? If I broadcast each key update separately – would the collection listener handle each update individually? |
I believe this would work yes, and the collection listener will handle each individual update. This would be the safest approach against causing regressions.
The collection updates get a little tricky because some collection mappings will be expecting the callback to be triggered with the entire collection, while others might be expecting the callback to be triggered with each object in the collection. This is dependent on the I think I prefer the first method because that will make the least amount of behavior changes for Onyx. |
|
I have read the CLA Document and I hereby sign the CLA |
|
@tgolen please make a sanity check when you have a minute, along with this comment. The only thing that bothers me is that one failing test. I don't fully understand the nature of those subscriptions, and based on the comment in code, that first call is expected to be with Lines 915 to 918 in 7cbf836 |
|
Wow, that is a great improvement! I don't think the test is too much to worry about. You can update the tests to change the Before your PR:
After your PR:
I think the important part of Does that all make sense? |
Yes, updated the test as you recommended – to verify that the first call for the non-existing keys is made with |
0628fe5 to
d0d261b
Compare
|
@tgolen how terrible is the idea to make the following change? case METHOD.MERGE_COLLECTION:
_.each(value, (_value, _key) => promises.push(() => merge(_key, _value)));
break; Lines 1560 to 1562 in 69b2d59 From what I understand, it will trigger the re-render of components listening to a collection N times? |
|
We use |
|
@tgolen I think I've made a working solution: it updates the data in the correct order, but it's a bit slower than the current version. Would it be possible to request a look from the side at the potential bottlenecks and improvements, especially the cache-related operations? |
|
OK, great. Let me ping some of our experts on Slack to see if they can have a look at this PR. You can also see my comment here about how it can be reliably tested to help protect against major regressions. |
kidroca
left a comment
There was a problem hiding this comment.
This Pull Request represents a positive step forward in enhancing performance by batching updates into a single persist-to-storage operation.
Considerations for follow-up work:
-
Refactoring for Code Deduplication: There's an opportunity to further refine our codebase by reducing duplicate logic. Specifically, methods like
setcould be simplified to merely forward calls toOnyx.updatewith the appropriate parameters. Similarly,Onyx.mergemight be refactored to leverageOnyx.updatefor batched updates. These changes would streamline our code and potentially simplify future maintenance. -
Optimizing Merge Logic: The current implementation's use of
multiGetto read current values seems to diverge from our optimized merge strategy that emphasizes delta merges. Previously, we focused on reading full values only as needed to facilitate updates viakeyChanged, while ensuring that only the delta of the merge was persisted to storage. The observed logic in this PR, which appears to send back the full JSON rather than just the delta, might warrant a reevaluation to ensure it aligns with our efficiency goals.
The overall changes introduced by this PR are commendable and contribute to ongoing efforts to improve performance.
|
Here it is: Expensify/App#44010 |
|
From the issue, it sounds like this can be closed for now. |
# Conflicts: # lib/Onyx.ts # lib/OnyxUtils.ts
|
Hey @tgolen, I'm both ashamed and happy to say that apparently the bust I encountered were because I configured the App incorrectly after being OOO for a long time. I'd like to reopen this PR and the related issue and give it a shot. |
|
Hey @tgolen @srikarparsi, could you please give this PR a final look and proceed to merge based on this test result? |
tgolen
left a comment
There was a problem hiding this comment.
Looks great, thank you! Here we go!
|
🚀Published to npm in v2.0.57 |
| if (operations[0] === null) { | ||
| promises.push(() => set(key, batchedChanges)); | ||
| } else { |
|
@paultsimura with these changes, does |
|
Hi @aldo-expensify, if we are talking about the operations within |
|
Nice, thanks! |
|
@paultsimura I found a bug in the version of When are we planning to release the changes made here? |
|
I'm planning to open a bump PR in the nearest days. |



Details
This PR changes how we process the
Onyx.updateoperation:mergeCollectioncall;mergecalls;Related Issues
Expensify/App#37560
Automated Tests
I've added tests that cover the
Onyx.update()with multiple merge operations of the same keys to verify that all the changes are batched into a singlemergeCollectionoperation.Manual Tests
Warning
Since this change alters one of the most common Onyx operations – we should perform a general regression testing of the system.
Onyx.update()operation with multiple updates of the same item inmergeandmergeCollectionoperations.Operation example ref:
react-native-onyx/tests/unit/onyxTest.js
Lines 1046 to 1103 in eb17a5d
Author Checklist
### Related Issuessection aboveTestssectiontoggleReportand notonIconClick)myBool && <MyComponent />.STYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)/** comment above it */thisproperly so there are no scoping issues (i.e. foronClick={this.submit}the methodthis.submitshould be bound tothisin the constructor)thisare necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);ifthis.submitis never passed to a component event handler likeonClick)Avataris modified, I verified thatAvataris working as expected in all cases)mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari
MacOS: Desktop