Refactor LocalCache to avoid calling Marshal.dump as much#42014
Conversation
The local cache need to protect against mutations of both the initial received value, and the value returned. See: - rails#36656 - rails#37587 Because of this, the overhead of the local cache end up being quite significant. On a single read, the value will be deep duped twice. So unless the value is one of the type benefiting from a fast path, we'll have to do two `Marshal.load(Marshal.dump(value))` roundtrips, which for large values can be very expensive. By using a specialized `LocalEntry` type instead, we can store the `Marshal` payload rather than the original value. This way the overhead is reduced to a single `Marshal.dump` on writes and a single `Marshal.load` on reads.
|
@casperisfine @byroot Before benchmarking, I created a very simple dataset with 200 users. Each user only includes name, phone. In rails 6.0.4In rails 6.1.0I see a serious speed degradation. So can you backport this PR into rails 6.1.3.3 or 6.1.4? Sorry, i can't benchmark the code if writing procedure code. |
|
@hoangtuyb96 please provide a proper benchmark script using Also I don't understand what you are expecting me to backport, this is a performance optimization, stable branches only receive bug fixes. |
|
Here's a benchmark script example: https://gist.githubusercontent.com/casperisfine/0ccd24dc209665c46e83bcc2920dd7dc/raw/76a18809873d188501b2583a7e4636f2e83c53fe/async.rb |
|
Thank you for the advice. |
|
I have just created issue. Please check it. |



Second attempt at: #41882
The local cache need to protect against mutations of both the initial received value, and the value returned.
See:
Because of this, the overhead of the local cache end up being quite significant. On a single read, the value will be deep duped twice. So unless the value is one of the type benefiting from a fast path, we'll have to do two
Marshal.load(Marshal.dump(value))roundtrips, which for large values can be very expensive.By using a specialized
LocalEntrytype instead, we can store theMarshalpayload rather than the original value. This way the overhead is reduced to a singleMarshal.dumpon writes and a singleMarshal.loadon reads.One downside however it increased complexity, mostly because
handle_expired_entrycan pass a local entry directly towrite_entry, so in such case we need to rebuild a regularCache::Entry.