-
Notifications
You must be signed in to change notification settings - Fork 69
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: Relax locking contention for cache and cachekv #353
Conversation
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## main #353 +/- ##
==========================================
- Coverage 55.41% 55.38% -0.04%
==========================================
Files 620 620
Lines 51694 51605 -89
==========================================
- Hits 28646 28581 -65
+ Misses 20964 20941 -23
+ Partials 2084 2083 -1
|
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 - it might make sense to do a gobench test before/after the change to help double-check the throughput increases (hard to eyeball, this could be much faster, not sure)
store/cachekv/store.go
Outdated
} else { | ||
value = cacheValue.Value() | ||
return store.parent.Get(key) |
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.
small nit, we can drop the else block here and just return
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.
True, will be fix
|
||
// We need a copy of all of the keys. | ||
// Not the best, but probably not a bottleneck depending. | ||
keys := make([]string, 0, store.cache.Len()) | ||
keys := []string{} |
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.
To avoid the allocations, keeping the size at 0 and capacity at store.cache.Len()
can actually be better.
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.
Yeah, but unfortunately, sync map doesnt support length, that's why we remove all length here
} | ||
} | ||
|
||
// Clear the cache using the map clearing idiom | ||
// and not allocating fresh objects. | ||
// Please see https://bencher.orijtech.com/perfclinic/mapclearing/ | ||
store.cache.DeleteAll() | ||
store.cache.Range(func(key, value any) bool { |
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.
side node: I wonder if it would make sense to just set the cache to a new map (requires concurrency protection) and let the garbage collector clean up the old one. This is logically fine.
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.
Yeah this is good questions, I've thought about that, but I think it could be risky, I'm not sure why initially we are not doing that tbh, so better to just keep the same logic first.
Describe your changes and provide context
Problem:
Currently when doing profiling, there are lot of locking contention happening in the cachekv layer, this is because we are using mutex for all read and write keys, but cachekv as a transient cache doesn't really need such a strict locking mechanism. Having a high locking contention would hurt the parallelize transaction execution performance a lot.
Solution:
Testing performed to validate your change
Fully tested in loadtest env