Skip to content
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

Perf: Lru cache #93

Merged
merged 11 commits into from
Aug 6, 2021
Merged

Perf: Lru cache #93

merged 11 commits into from
Aug 6, 2021

Conversation

kidroca
Copy link
Contributor

@kidroca kidroca commented Aug 3, 2021

cc @marcaaron

Details

PR4. Of the changes planned here: #88 (comment)

Keep a list of recently accessed keys in OnyxCache - recentKeys
This serves to clean "Least Recently Used" keys from cache

Keys are added to recentKeys for any cache access operation - get/set/merge

Added a configurable limit of 150 keys in cache
This looks like a sane value from observations during chat browsing

Note on print/benchmark updates:

This helps with extracting information to excel in a consistent matter

Add an optional methods list parameter
When such a list is provided only the methods from this list will be printed

Removed any sorting
Tables order varies and makes it harder to deal with in Excel

Related Issues

Expensify/App#2667

Automated Tests

Added test covering that recent keys are available in cache
and that older keys past the LRU limit are cleared from cache when safe eviction connection happens

Linked PRs

kidroca added 2 commits August 3, 2021 22:23
This helps with extracting information to excel in a consistent matter

Add an optional `methods` list parameter
When such a list is provided only the methods from this list will be printed

Removed any sorting
Tables order varies and makes it harder to deal with in Excel
Keep a list of recently accessed keys in `OnyxCache` - `recentKeys`
This serves to clean "Least Recently Used" keys from cache

Keys are added to `recentKeys` for any cache access operation - get/set/merge

Added a configurable limit of 150 keys in cache
This looks like a sane value from observations during chat browsing
Copy link
Contributor

@marcaaron marcaaron left a comment

Choose a reason for hiding this comment

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

Just leaving some comments as this is still in draft.

lib/Onyx.js Outdated
@@ -742,13 +689,16 @@ function mergeCollection(collectionKey, collection) {
* @param {function} registerStorageEventListener a callback when a storage event happens.
* This applies to web platforms where the local storage emits storage events
* across all open tabs and allows Onyx to stay in sync across all open tabs.
* @param {Number} [options.maxCachedKeysCount=150] Sets how many recent keys should we try to keep in cache
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a blocker, would maybe remove the =150 here since it's possible this could go out of date if someone changes the default at the top of the file.

lib/OnyxCache.js Outdated Show resolved Hide resolved
lib/Onyx.js Show resolved Hide resolved
kidroca added 4 commits August 4, 2021 19:02
Having aliases with no data captured prints empty methods/tables
We shouldn't be in a hurry to clean up cache, we can clean it up periodically
every 10, 30 or 60sec. or on another event that happens less frequently
lib/Onyx.js Outdated
Comment on lines 705 to 708
setInterval(() => {
// Try to free anything dated from cache
cache.removeLeastRecentUsedKeys(maxCachedKeysCount);
}, 10 * 1000);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Created this interval based cleanup because trying to free cache after each disconnect is too often
For example when I switch to a new chat - 10 old connections would disconnect at the same time and cause 10 cache cleans, where only 1 would be enough

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Instead of interval based solution perhaps we can hook to something that happens less frequently
I think it would be OK to clean cache every minute or even less frequently as long as we clean it

Copy link
Contributor

Choose a reason for hiding this comment

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

I feel maybe the interval based solution is too aggressive since most of the time we don't really need to clean the cache at all.

What if we hook the cache clean into the connection of a safe eviction key like the reportActions_*. If we are connecting to another large object that seems like a good opportunity to clean the cache in preparation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the idea, that sounds much better, will update

lib/OnyxCache.js Outdated Show resolved Hide resolved
lib/decorateWithMetrics.js Outdated Show resolved Hide resolved
kidroca added 3 commits August 4, 2021 20:36
Removed tests covering the old behavior wehere a connection would keep data in cache
Added tests that cover periodic cache cleanup and the new LRU functionality
…irst usage"

This reverts commit d34cbb6.
The functionality is actually needed to prevent duplicate decorations
Remove the setInterval cache cleanup solution
Instead try to free cache when a new connection to
safe eviction key happens
This shouldn't happen very often and as such is
ideal for cleaning cache
@kidroca
Copy link
Contributor Author

kidroca commented Aug 5, 2021

Updated
Replaced the set interval cache clean trigger with cache clean triggered on safe eviction keys connections

@kidroca kidroca requested a review from marcaaron August 5, 2021 22:30
@kidroca kidroca marked this pull request as ready for review August 5, 2021 22:30
@kidroca kidroca requested a review from a team as a code owner August 5, 2021 22:30
@MelvinBot MelvinBot requested review from tylerkaraszewski and removed request for a team August 5, 2021 22:31
@kidroca
Copy link
Contributor Author

kidroca commented Aug 6, 2021

I've moved to test the changes here on the latest Expensify
But after capturing some metrics I see there's no much difference in before/after

The cache already contains everything from storage (For the Before run)
image

But my chat list is small - I've got only 10

Here's why the cache already contains everything from storage

  1. This connection loads all chat actions and is kept for the duration of the app - the old cache logic does not remove items that are connected: https://github.com/Expensify/App/blob/e88b7f984ab4e89e33844260c3a334622bf23023/src/libs/actions/ReportActions.js#L20-L22
  2. General report data used in LHN connects to all report items (and other keys)
  3. Recent optimizations keep some keys in cache forever
    • The OnxyProvider related keys (network, personalDetails, report action drafts)
    • The initial keys (sesson, account, network, IOU, is sidebar loaded)

The changes in this PR set a default limit of 55 recent keys in cache so they should help reduce the cache footprint for users that have more than 10 Chats (With 10 chats I have 58 unique keys in storage)

I've explained why we don't need to keep items in cache just because they have an active connection here: #93 (comment)

In short if something is "so old" yet still connected after we've cycled 100-150 keys and it didn't re-appear at least once to revive itself - it's not something that will benefit from the "cache while connected" strategy - no other connection/component wants to use it anyway


As a summary the changes here don't seem to provider any performance benefit over the existing strategy, but they are simplifying the code and can help with freeing more cache

Copy link
Contributor

@marcaaron marcaaron left a comment

Choose a reason for hiding this comment

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

Thanks for the breakdown. I don't mind merging these changes anyway since they are an improvement over the previous logic.

Should we get these changes into Expensify/App?

@marcaaron marcaaron merged commit d7553b9 into Expensify:master Aug 6, 2021
@kidroca
Copy link
Contributor Author

kidroca commented Aug 6, 2021

Should we get these changes into Expensify/App?

I can open a PR and test all platforms on Monday

@kidroca
Copy link
Contributor Author

kidroca commented Aug 9, 2021

@marcaaron I've Opened this PR: Expensify/App#4509 to update Onyx version in Expensify/App

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants