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

Use Store Cache and merge optimistic cache with Memory cache #5651

Merged
merged 18 commits into from Mar 7, 2024
Merged

Conversation

BoD
Copy link
Contributor

@BoD BoD commented Feb 23, 2024

Replace our in-house LRUCache that required guarding with a lock with the Cache from the Store project (which is a KMP port of Guava's Cache).

Benchmarks after this change looked promising, but we still used a lock upstream inside OptimisticCache which reduced these gains.

To improve on that, this PR also merges the optimistic cache with the memory cache, and uses a thread-safe map (backed by ConcurrentHashMap on the JVM and a regular map behind a lock on other platforms) to store the RecordJournals.

CacheIncubatingTests cacheOperationMemory cacheResponseMemory concurrentCacheOperationMemory concurrentCacheResponseMemory
Before 87,918,019 82,278,491 210,223,423 213,267,240
After 80,766,616 80,221,426 42,095,053 39,544,423
Improvement 8.13% 2.50% 79.98% 81.46%
ApolloStoreIncubatingTests concurrentReadWritesMemory
Before 45,964,577
After 18,559,416
Improvement 59.62%
CacheIncubatingIntegrationTests concurrentQueriesTestNetworkTransportMemory
Before 47,139,692
After 38,322,750
Improvement 18.70%

A few changes to get there:

  • NormalizedCache is now an interface, and chaining is removed (MemoryCache still supports chaining)
  • OptimisticNormalizedCache is now an interface, implemented by MemoryCache and OptimisticNormalizedCacheWrapper (new name for what OptimisticCache was)

The Store cache was copied to the project, but I'll see if we can depend on it instead - I had to make one addition: the getAllPresent() fun, also the wasm target is missing - so opening in draft for now.

Copy link

netlify bot commented Feb 23, 2024

Deploy Preview for apollo-android-docs canceled.

Name Link
🔨 Latest commit 5099a49
🔍 Latest deploy log https://app.netlify.com/sites/apollo-android-docs/deploys/65e880d130f8a4000837a6f3

Comment on lines 75 to 64
result = result && remove(CacheKey(cacheReference.key), true)
result = result || remove(CacheKey(cacheReference.key), true)
Copy link
Contributor

Choose a reason for hiding this comment

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

I was going to say this looks like it needs to be backported to non-incubating but sounds like the whole semantics of the return value need to be refined here, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah the && didn't make much sense to me but if so the doc should also be updated to be precise:

- * @return `true` if a record with such key was successfully removed, `false` otherwise
+ * @return `true` if at least one record was successfully removed, `false` otherwise

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes 👍 Do we have a list of such API tweaks we need to go through?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not yet - I could open a dedicated ticket

Comment on lines -26 to +27
var size = SIZE_OF_RECORD_OVERHEAD + record.key.utf8Size().toInt()
var size = SIZE_OF_RECORD_OVERHEAD + record.key.length
for ((key, value) in record.fields) {
size += key.utf8Size().toInt() + weighField(value)
size += key.length + weighField(value)
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the reasoning for changing the weighter implementation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes sorry this is a totally unrelated to this PR! I just noticed this and thought in memory strings are stored either in utf-16 or some sort of compact form that's iso-latin-1 if possible (apparently since JDK9 - I can't tell if this applies to Android). I think it's never utf-8. But length*2 may be a better value if we think it's probably utf-16.

And now the fun question: what is it on apple/js/wasm? 😅

Copy link
Contributor

Choose a reason for hiding this comment

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

O(length) is probably the only cross-platform answer... So if we want that to be correct I'm guessing we need per-platform weighter.

My hunch is that the long term solution is to memory cache btree tables more than Record this way the same computation applies to both in-memory and persistent.

Comment on lines 236 to 238
val dump = apolloStore.dump().filterKeys {
// Ignore optimistic cache for comparison
it != OptimisticNormalizedCache::class && it != OptimisticNormalizedCacheWrapper::class
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the reason those might be diferent?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

MemoryCache uses OptimisticNormalizedCache::class as a key in its dump() fun. Maybe not the most intuitive thing?

Copy link
Contributor

Choose a reason for hiding this comment

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

Gotcha 👍 Yea, not the best API. Add it to the list of APIs to bikeshed?

@BoD BoD marked this pull request as ready for review March 6, 2024 13:41
@BoD
Copy link
Contributor Author

BoD commented Mar 7, 2024

Merging this for now. I've opened #5689 to circle back if we can use Store as a dependency instead of including it.

@BoD BoD merged commit aa3e43c into main Mar 7, 2024
9 checks passed
@BoD BoD deleted the cache-store branch March 7, 2024 08:03
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.

None yet

2 participants