-
Notifications
You must be signed in to change notification settings - Fork 11.5k
opt: memo cache write operations override store values #56481
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
opt: memo cache write operations override store values #56481
Conversation
The failing tests are not related to my changes. |
Want to get a review from @timacdonald on this one 👍 |
@timacdonald do you think this should go to |
If the inconsistencies are addressed, it should not be a breaking change, so If the inconsistencies are not addressed, I think we should either not move forward or target All for the feature in principle. |
Thanks for the feedback @timacdonald
Regarding this part: public function test_it_is_inconsistent()
{
foreach (['redis', 'database', 'memcache'] as $name) {
Cache::store($name)->increment('count');
$this->assertSame('1', Cache::store($name)->get('count'), $name);
}
} I might be missing out something, but IMO this issue isn't introduced by my changes, since it already exists in the framework. The type inconsistency with increment() (and other similar ones) would occur even if we are not using cache memoization. Therefore, we just need to make sure the memoized value types are always matching the types that the underlying cache store would return. To test whether memoization behaves consistently with the underlying store, a test like this might be more relevant: public function test_it_is_inconsistent()
{
foreach (['redis', 'database', 'memcache'] as $name) {
Cache::store($name)->increment('count');
$memo = Cache::memo($name)->get('count');
$live = Cache::store($name)->get('count');
$this->assertSame($memo, $live, $name);
}
} This ensures that the memoized value and the value from the underlying cache store are always matching. Let me know what you think :) |
@timacdonald I've pushed the test I've mentioned in my previous comment. I think it would be enough to ensure that the memoized value is always the same type as the type of the value fetched from the underlying cache store. Also a similar test, without using the memo functionality, would fail because not all cache stores return stringy values. This proves that the changes introduced in this PR do not affect this already existing issue. // ❌ fails
public function test_prove_cache_store_type_inconsistencies()
{
foreach (['redis', 'database', 'memcached'] as $name) {
Cache::store($name)->put('count', 0);
Cache::store($name)->increment('count');
$live = Cache::store($name)->get('count');
$this->assertSame("1", $live, "Failed for cache store: $name");
}
} |
@KIKOmanasijev, I agree my initial test was bad. Sorry about that. It has been a while since I did this so I had forgotten what the exact issue was. Here is a better example where the memo value differs from the underlying cache store: <?php
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
Cache::put('count', 0);
Cache::memo('database')->increment('count');
dump(
Cache::store('database')->get('count'),
Cache::memo('database')->get('count')
);
}); ![]() This was produced using your current changes. |
@timacdonald I spent a lot of time into thinking about a potential solution for this problem, but the problem is more complex than it seems. All default cache stores (except Redis) return numeric values when using get(), and they all returns numeric values when using increment()/decrement(). So the return types for get/increment all matching for all drivers, and the only outlier is the Redis store. For me, it's confusing that "getting" a value would ALWAYS return a string, and incrementing/decrementing a value would ALWAYS return an integer (although this is a default Redis behaviour). One solution that came to my mind was to check whether the underlaying store is a RedisStore instance, but something felt off because it introduced driver-specific logic and it didn't seem future-proof. Instead, I propose updating Redis' implemention of the increment() and decrement() methods to cast their return values to strings, thus the return types will always match when getting a value. I am aware this is a breaking change, but I think the benefits of matching the return types of the increment and decrement operations with the get operation for Redis-based cache stores, is worth the trade-off. What do you think?
|
Not sure juice is worth the squeeze here. |
Hello,
I was just watching @taylorotwell's Keynote at Laracon, and saw him presenting an example about the new memo() cache features. It was this example, to be more precise:
Current implementation
So this example got me wondering, why do we need to have 3 roundtrips to the cache store? Why is the
Cache::memo()->put()
method not memoizing the value?Unnecessary Cache Roundtrips
The issue I see is that this will make 3 roundtrips to the underlying cache store, because the put() command invalidates the existing memoized value. Instead of storing invalidating the cache, I think overriding the existing value would make more sense, and it could save an extra roundtrip request to the cache store.
After the proposed solution
Disclaimer:
master
branch.Note: