-
Notifications
You must be signed in to change notification settings - Fork 75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: Onyx Cache #76
Conversation
Fixed an issue with merge collections to cache
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are still some things to complete
Like clearing cached keys when there are no subscribers for a given key
Some existing tests started to fail after the update, I'll take a look tomorrow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just leaving some initial comments since it seems like you are still working through stuff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the review @marcaaron
Added some replies
…ys in the cache service
61c0c82
to
5435063
Compare
Updated |
Addressed the outstanding comment by explicitly converting the set to array |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, will leave open to let @Julesssss get his review in
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-tested this today on the latest E.cash main on an account that has an IOU and I'm getting this error on both web and desktop after signing in (iOS/Android work fine):
TypeError: Currency code is required with currency style.
at new NumberFormat (<anonymous>)
at oO (app-6bf1371e025ae4112ce5.bundle.js:78)
at Object.numberFormat (app-6bf1371e025ae4112ce5.bundle.js:78)
at IOUBadge (app-6bf1371e025ae4112ce5.bundle.js:78)
at Yi (app-6bf1371e025ae4112ce5.bundle.js:50)
at gs (app-6bf1371e025ae4112ce5.bundle.js:50)
at lc (app-6bf1371e025ae4112ce5.bundle.js:50)
at sc (app-6bf1371e025ae4112ce5.bundle.js:50)
at Zs (app-6bf1371e025ae4112ce5.bundle.js:50)
at app-6bf1371e025ae4112ce5.bundle.js:50
Uncaught (in promise) TypeError: Currency code is required with currency style.
at new NumberFormat (<anonymous>)
at oO (app-6bf1371e025ae4112ce5.bundle.js:78)
at Object.numberFormat (app-6bf1371e025ae4112ce5.bundle.js:78)
at IOUBadge (app-6bf1371e025ae4112ce5.bundle.js:78)
at Yi (app-6bf1371e025ae4112ce5.bundle.js:50)
at gs (app-6bf1371e025ae4112ce5.bundle.js:50)
at lc (app-6bf1371e025ae4112ce5.bundle.js:50)
at sc (app-6bf1371e025ae4112ce5.bundle.js:50)
at Zs (app-6bf1371e025ae4112ce5.bundle.js:50)
at app-6bf1371e025ae4112ce5.bundle.js:50
This results in a white screen on Web and Desktop. I tested this on dev after running npx react-native-clean-project
and wiping the node_modules folder, and I also confirmed it happens when running npm run desktop-build
as well. Here's the steps I did to test this, just to make sure I'm not missing anything:
- Run
npx react-native-clean-project
and wipe node_modules folder - Update package.json
react-native-onyx
commit hash to77b07e10dfd10831380c197a132a032e812b31f9
- Run npm install then npm run web and npm run desktop and npm run desktop-build
- Sign into an account that has an IOU
- See the above errors in the console and a white screen
- Clear changes in package.json so the
react-native-onyx
hash is back tod0a5d12c08f2f029e3038fb3516a93a9403e9746
- Run
npx react-native-clean-project
and wipe node_modules folder - Run npm install then npm run web and npm run desktop and npm run desktop-build
- Confirm there is no white screen and the error doesn't appear after signing in
I chatted w/ @roryabraham and he is also going to test this to confirm
It looks like setting a default value for currency here fixes the issue, going to create an E.cash PR. |
The e.cash PR has been merged, going to test again on the main branch to confirm everything is working |
Woo!!!!
…On Fri, Jun 4, 2021 at 2:25 PM Joe Gambino ***@***.***> wrote:
The e.cash PR has been merged, going to test again on the main branch to
confirm everything is working
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#76 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAEMNUWYIQEA4Z3FWTESQDTTRFAD7ANCNFSM45QMDVUQ>
.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spent a while testing this on iOS, web, desktop, and I couldn't find any regressions or obvious errors. Seems to work well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me as well, tested on all platforms and it's working great! I'll let @marcaaron have a final review/merge for this one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the late review! I ran this locally and while it was tricky to do a visual comparison between dev and prod builds the improvement is noticeable.
I had some of the same concerns as @Jag96 mentioned here, but as discussed there are further optimizations that might take care of this. The main one is that even pages without huge lists feel slightly slower to display. For example, the initial delay when tapping an option in the setting page feels like input lag (I know this isn't actually the case, but this might seem like input lag to a user). Also, it would be great to clear up the issue where FlatList items are added one by one.
Anyway, thanks for leading this huge improvement. I look forward to testing on a prod build!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a few minor comments (mostly about tests and not blockers - but should be addressed). Still think we should move usages of captureTask
and AsyncStorage
into the cache itself, but it's not a hill I will die on so I'll let it go for now.
import ViewWithCollections from '../components/ViewWithCollections'; | ||
|
||
describe('Onyx', () => { | ||
describe('Cache Service', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NAB, appreciate the thoroughness here, but rather than test the individual cache methods we could have just tested the behavior that we'd expect when Onyx is actually using the cache vs. using the cache in isolation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NAB, appreciate the thoroughness here, but rather than test the individual cache methods we could have just tested the behavior that we'd expect when Onyx is actually using the cache
Actually we've tested both in isolation here and the behavior that we'd expect when Onyx is using cache (in the next section)
It's much harder to test all the expectations written here in Onyx, you have to
- setup component(s)
- configure async blocks and flush promises at specific times
- not as straightforward if you want to debug a test
The test for Onyx validate complete flows covering multiple cache methods - e.g. a key is not retrieved from storage when a rendered component uses it
/** @type OnyxCache */ | ||
let cache; | ||
|
||
// Always use a "fresh" instance |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NAB, Is that what requiring again would do? When would this not be creating a "fresh" instance? Why should we use one instead of just clearing out the cache?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NAB, Is that what requiring again would do? When would this not be creating a "fresh" instance? Why should we use one instead of just clearing out the cache?
Simply requiring again would not do the trick. It would return the instance that was already created from the first time the script was required somewhere
There's no function that completely clears the cache. You could implemented something here or in the cache file, but if it's only for the test you could just tell jest you want a clean env
Next we will just need to create a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still think we should move usages of captureTask and AsyncStorage into the cache itself, but it's not a hill I will die on so I'll let it go for now.
Sorry I thought that's what you meant by #76 (comment)
Thanks for your thoughts. Let's go ahead and make this change.
I thought it was a reply to
Adding Onyx specifics here makes this util less flexible it should just provide some low level cache handling heplers like hasCacheForKey, get, set, merge and the resolveTask helper for concurrency calls and leave the caller the flexibility to use it however needed. Then it's transparent how items are retrieved in Onyx.js and the cache handling is on the same abstraction level
And so I switched to how Cache was set up initially
Right now we have separation - cache logic in Cache. Onyx logic and flows in Onyx. It's transparent how Onyx uses and updates cache
It would be confusing to me if I read the high level of the code for Onyx.get
and see that it just does return cache.get(key)
. "What happens if there's no cache?" is my first question, then I have to open OnyxCache
and have another question "Why does cache knows how to retrieve values from storage?"
From the current code it's clear that it doesn't have to know and works
If it's just a Proxy - the "startTask" implementation where it doesn't know how the task would provide the value is OK, but it made it harder to test and it was confusing for everybody but me
import ViewWithCollections from '../components/ViewWithCollections'; | ||
|
||
describe('Onyx', () => { | ||
describe('Cache Service', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NAB, appreciate the thoroughness here, but rather than test the individual cache methods we could have just tested the behavior that we'd expect when Onyx is actually using the cache
Actually we've tested both in isolation here and the behavior that we'd expect when Onyx is using cache (in the next section)
It's much harder to test all the expectations written here in Onyx, you have to
- setup component(s)
- configure async blocks and flush promises at specific times
- not as straightforward if you want to debug a test
The test for Onyx validate complete flows covering multiple cache methods - e.g. a key is not retrieved from storage when a rendered component uses it
/** @type OnyxCache */ | ||
let cache; | ||
|
||
// Always use a "fresh" instance |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NAB, Is that what requiring again would do? When would this not be creating a "fresh" instance? Why should we use one instead of just clearing out the cache?
Simply requiring again would not do the trick. It would return the instance that was already created from the first time the script was required somewhere
There's no function that completely clears the cache. You could implemented something here or in the cache file, but if it's only for the test you could just tell jest you want a clean env
Should I create this? |
No. I was thanking you for your thoughts and requesting that you take my suggestion anyway. |
Moving this comment here, as the linked issue was locked. This morning @trjExpensify and I noticed a potential regression with the Onyx changes: The While the simple solution would be to add a conditional check to confirm the iou object is valid, we need to confirm whether this requires a fix, or is now expected behavior. CC @kidroca IOURequestOnyxCrash.mov |
It looks like the cleanup PR supposedly resolved this issue. But as far as I can tell, these commits are also on the staging build. So it seems to me that the issue is still occurring:
|
I'll investigate Lines 723 to 724 in 9b03d95
Are we supposed to put these // Set our default key states to use when initializing and clearing Onyx data
defaultKeyStates = initialKeyStates;
cache.merge(initialKeyStates); I think they automatically go there: Lines 730 to 731 in 9b03d95
This function would internally call Maybe, this happens after enough delay that the initial value is Anyway, I'll see what can I find |
Thanks, @kidroca! We're imminently sending a newsletter out to all our users to announce the IOU features and welcome them into E.cash to test out what we’ve been built so far. We shipped the latest prod build off to the app stores last night for review, which was the last item on the list before hitting send. If this regression makes it to production, it delays our timeline for the announcement, as the IOU feature will be broken on Web/Desktop/mWeb. With that, it would be awesome if we could get a fix up ASAP to minimise the delay. Thanks again! |
Yeah, I think that would make sense. Perhaps we should just expect these to be initially null... but I think having the defaults added to cache would be preferable. |
I traced what is causing the issue. It's our cache cleanup logic and a bug in
Lines 373 to 382 in 9b03d95
react-native-onyx/lib/OnyxCache.js Lines 91 to 98 in 9b03d95
It should just drop the cached @Julesssss I'll open a PR shortly |
`OnyxCache.remove` was renamed to `drop` and updated so that it does not remove the `key` but only the value This way the result of `getAllKeys` is unaffected Related to Expensify#76
@Julesssss pushed a fix here #81 And confirmed it against the Expensify.cash.-.Google.Chrome.2021-06-09.18-19-25.mp4 |
@marcaaron, @tgolen
Details
Added a new file and defined
OnyxCache
class to encapsulate cache related methods and have some reuseUpdate Onyx methods to use cache
Updated Onyx methods to deal with concurrent method calls while data is still being read
Added tests
Related Issues
GH_LINK
Fixes #63
Expensify/App#2762
Automated Tests
Added test annotated with GIVEN, WHEN, THEN. They should be pretty self explanatory
Covering
OnyxCache
and the integration in Onyx and AsyncStorage callsLinked PRs