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

[fix/feat] recache values in higher layers for composed cache adapter #182

Merged
merged 5 commits into from
Jul 28, 2022

Conversation

quinlanj
Copy link
Member

@quinlanj quinlanj commented Jul 27, 2022

Why

This PR recaches the values of lower cache layers missing from the higher layers of a ComposedCacheAdapter.

Consider a ComposedCacheAdapter with a local memory cache and a redis cache. In www, values typically stay in Redis much longer than they stay in Local Memory (capped to 5 seconds). In the scenario that a value is already cached in Redis but no longer cached in Local Memory (either it expired or was not the machine that originally cached to Redis), we will consider this a cache hit. Because the only time we cache values is when we fetch from DB, we will continually fetch from Redis until the value is evicted, at which point we will cache back into all the layers.

A better system would be to recache the higher layers with values found in the lower layers so we aren't loading from Redis most of the time.

How

recache higher layers in loadManyAsync

Test Plan

  • added new test

@codecov
Copy link

codecov bot commented Jul 27, 2022

Codecov Report

Merging #182 (804ee7a) into main (ce0ac6f) will increase coverage by 0.04%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##             main     #182      +/-   ##
==========================================
+ Coverage   96.23%   96.27%   +0.04%     
==========================================
  Files          80       80              
  Lines        1991     2015      +24     
  Branches      239      244       +5     
==========================================
+ Hits         1916     1940      +24     
  Misses         75       75              
Flag Coverage Δ
integration 96.27% <100.00%> (+0.04%) ⬆️
unittest 96.27% <100.00%> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
packages/entity/src/ComposedEntityCacheAdapter.ts 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update ce0ac6f...804ee7a. Read the comment docs.

packages/entity/src/ComposedEntityCacheAdapter.ts Outdated Show resolved Hide resolved
}
}
unfulfilledFieldValues = newUnfulfilledFieldValues;
}

// Recache values from higher layers that were not found in lower layers
for (let i = 0; i < this.cacheAdapters.length; i++) {
Copy link
Member

Choose a reason for hiding this comment

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

we should be able to run a Promise.all here instead of awaiting within a for loop since the re-cache operations are logically independent

Copy link
Member Author

@quinlanj quinlanj Jul 27, 2022

Choose a reason for hiding this comment

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

looking at the other code, we've made a point to await on caches closest to the database, then move onto the ones closer to the application. I think this was to avoid bugs where the cache closest to the db was stale, but the ones closer to the application was more up-to-date.

I think the most correct approach would be to iterate backwards (ie) for (let i = this.cacheAdapters.length - 1; i >= 0; i--) { to keep this behaviour consistent

Copy link
Member

Choose a reason for hiding this comment

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

Ah right. Good call.


let unfulfilledFieldValues = fieldValues;
for (const cacheAdapter of this.cacheAdapters) {
for (let i = 0; i < this.cacheAdapters.length; i++) {
Copy link
Member

Choose a reason for hiding this comment

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

We await within this loop to fetch less objects each time, though might it actually be faster to just fetch all from all caches in parallel as well and then merge them? Might be worth profiling. If we do stick with the for loop we should probably have an early break when unfulfilledFieldValues is empty.

Copy link
Member Author

Choose a reason for hiding this comment

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

Performance here will depend on the type of caches that are in the ComposedEntityCacheAdapter. In our case where we've got a local memory and redis cache, I would strongly prefer to keep the for-loop.

If we fetched everything in parallel, we will be waiting for the slowest cache every single time (redis). In the case we have a popular query and the caches are warm, we'll be waiting on the redis cache even though all the values are available in local memory. This would be unfortunate because the redis cache is magnitudes slower than the local memory cache.

The for-loop will optimize for the case when the cache is warm (which will be the majority of requests for a popular query), since we will only be waiting on the local memory cache

Copy link
Member

Choose a reason for hiding this comment

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

Good call. Yeah that makes sense.

We should still be able to get away with an early break out of the loop if unfulfilledFieldValues is empty though I think.

quinlanj and others added 2 commits July 27, 2022 21:10
Co-authored-by: Will Schurman <wschurman@gmail.com>
@quinlanj quinlanj marked this pull request as ready for review July 27, 2022 20:27
@quinlanj quinlanj requested a review from wschurman July 27, 2022 20:28
@quinlanj quinlanj changed the title [fix/feat] recache values in lower layers for composed cache adapter [fix/feat] recache values in higher layers for composed cache adapter Jul 27, 2022
Copy link
Member

@wschurman wschurman left a comment

Choose a reason for hiding this comment

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

Just a few minor changes, lgtm otherwise though.

packages/entity/src/ComposedEntityCacheAdapter.ts Outdated Show resolved Hide resolved
packages/entity/src/ComposedEntityCacheAdapter.ts Outdated Show resolved Hide resolved
quinlanj and others added 2 commits July 28, 2022 03:24
Co-authored-by: Will Schurman <wschurman@gmail.com>
@quinlanj quinlanj merged commit 8530fb0 into main Jul 28, 2022
@quinlanj quinlanj deleted the @quin/recacheLowerLayers branch July 28, 2022 01:46
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