-
Notifications
You must be signed in to change notification settings - Fork 11.5k
[12.x] Introduce memoized cache driver #55304
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
Conversation
if (! $this->app->bound($bindingKey = "cache.__memoized:{$driver}")) { | ||
$this->app->scoped($bindingKey, fn () => $this->repository( | ||
new MemoizedStore($driver, $this->store($driver)), ['events' => false] | ||
)); | ||
} |
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.
We bind the memo drivers to the container as scoped instances so they are refreshed in Octane and the queue worker.
Isn't this a more specific (limited?) implementation of a two-level cache? Like two drivers used one after nother? So an So |
I think you're right. The difference is that he simplified it. |
That's really cool. Is there a possibility of adding a Cache::put('name', 'Taylor');
Cache::get('name'); // "Taylor"
Cache::put('name', 'Tim');
Cache::get('name'); // "Tim"
Cache::put('name', 'Taylor');
Cache::memo()->get('name'); // "Taylor"
Cache::put('name', 'Tim');
Cache::memo()->get('name'); // "Taylor"
Cache::memo()->refresh();
Cache::memo()->get('name'); // "Tim" |
Any thoughts related to tests? 🤔 |
@tpetry, I can see a multi-level cache being a good addition to the framework. That being said, I don't see this as the same thing. I would expect a multi-level cache to respect the TTL up and down the cache stack. If we had a cache stack implementation, e.g., I feel that I'm not entirely sure how common it is for Laravel applications to have multiple cache levels. Is this a pattern you've commonly used or seen in Laravel applications? |
The memo driver will be flushed between tests as each instance is bound to the container.
Can you share some real-world code where doing that is something you would want? I can see it is interesting but I can't see where I would use it off the top of my head. |
I am using user-land implementation of the 2-level cache in production now. Great feature indeed.
Straying from decorator, why not add a |
Otherwise, considering repository implementation consider null as not cached and the |
Considering the primary objective of this PR, A real example that I can take from one of my software is an auditing log concept, which as actions occur in the life cycle, I store them and gradually update them so that only at the |
I completely agree with @tpetry. This seems like an extra layer of caching on top of the existing one, which is uncommon and may lead developers to question why there's a cache for something Laravel already handles out of the box. It also introduces additional overhead—more logic and processing time just to check if a cached value exists. On top of that, it could encourage even more unnecessary layers in the future. |
@timacdonald can I just say that I love how elaborate your PR descriptions are. 🔥 |
Thank you, @Treggats |
Does this driver proxy all calls to the underlying driver? Including if I call the |
The So at the end when calling |
@rodrigopedra Thanks! |
Thanks @timacdonald! Nice feature. I was wondering why the For the sake of testing I just patched the |
oOh wow @timacdonald. This is very nice! |
If anyone needs it; a multi level cache implementation: https://github.com/rapidez/laravel-multi-cache |
We put things in the cache because the cache is fast; that doesn't mean hitting the cache is free.
This PR introduces a memoized cache driver. This new driver is a decorator around other cache drivers. It will remember values resolved from the cache and store them in memory for the remainder of the execution.
The following illustrates the values returned:
The
memo
function accepts a driver name. This indicates the real driver that the memo driver should decorate:Each driver will get a unique memo decorator. This means values are not shared between different drivers:
When you call a method on the memo driver that is intended to mutate the value in the cache, e.g.,
put
,remember
, etc., we forget the memoized value and then call the same method on the decorated driver:Note
I had hopes and dreams that if you wrote to the cache via the memo driver we could actively remember the value. That way you don't need to retrieve it after a write. Unfortunately there were too many inconsistencies between drivers and different mutation methods for that to work. For example, when you call increment the value returned from the cache is an int. If you then retrieve that value via
get
it will be a string of the int.The memo driver does not fire events. Events will be fired by the underlying driver if it is called.
When calling methods that mutate the cached value, the memo driver will always forget memoized value even if the call to the underlying driver fails.
One last bonus benefit of the memo driver is that is ensures consistent values from the cache throughout a request. Imagine you attempt to get the same key in two different places within a request and you get two different results because a different process updated the value or perhaps it just TTLd out of the cache. That could cause unexpected runtime side effects on your application. Using
Cache::memo
ensures that you get the same result for the entire request or job lifecycle regardless of what happens in the cache while time progresses throughout the execution.This idea has been bubbling away for a while and once we saw what was happening in some of our customers’ apps via Laravel Nightwatch, I knew we needed to make it happen.