Skip to content

Commit 3964104

Browse files
author
hayase_yasuhiro
committed
Fix Cache::flexible() to work with Redis Cluster
The flexible() method was generating keys that could hash to different slots in Redis Cluster, causing CROSSSLOT errors. This fix adds Redis hash tags to ensure all related keys (value, created timestamp, and lock) are stored in the same slot. Changes: - Add a 4-character MD5 hash prefix as a hash tag for slot allocation - Wrap hash in {} to ensure consistent slot placement in Redis Cluster - Maintain original key structure for non-cluster Redis instances - Works with both phpredis and predis drivers This resolves issues with AWS ElastiCache Serverless (Valkey) and other Redis Cluster environments while remaining backward compatible with standalone Redis instances.
1 parent 0fc5528 commit 3964104

File tree

1 file changed

+16
-11
lines changed

1 file changed

+16
-11
lines changed

src/Illuminate/Cache/Repository.php

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -488,35 +488,40 @@ public function rememberForever($key, Closure $callback)
488488
*/
489489
public function flexible($key, $ttl, $callback, $lock = null, $alwaysDefer = false)
490490
{
491+
$hashKey = substr(md5($key), 0, 4);
492+
$valueKey = "{{$hashKey}}:{$key}";
493+
$createdKey = "{{$hashKey}}:illuminate:cache:flexible:created:{$key}";
494+
$lockKey = "{{$hashKey}}:illuminate:cache:flexible:lock:{$key}";
495+
491496
[
492-
$key => $value,
493-
"illuminate:cache:flexible:created:{$key}" => $created,
494-
] = $this->many([$key, "illuminate:cache:flexible:created:{$key}"]);
497+
$valueKey => $value,
498+
$createdKey => $created,
499+
] = $this->many([$valueKey, $createdKey]);
495500

496501
if (in_array(null, [$value, $created], true)) {
497502
return tap(value($callback), fn ($value) => $this->putMany([
498-
$key => $value,
499-
"illuminate:cache:flexible:created:{$key}" => Carbon::now()->getTimestamp(),
503+
$valueKey => $value,
504+
$createdKey => Carbon::now()->getTimestamp(),
500505
], $ttl[1]));
501506
}
502507

503508
if (($created + $this->getSeconds($ttl[0])) > Carbon::now()->getTimestamp()) {
504509
return $value;
505510
}
506511

507-
$refresh = function () use ($key, $ttl, $callback, $lock, $created) {
512+
$refresh = function () use ($valueKey, $createdKey, $lockKey, $ttl, $callback, $created) {
508513
$this->store->lock(
509-
"illuminate:cache:flexible:lock:{$key}",
514+
$lockKey,
510515
$lock['seconds'] ?? 0,
511516
$lock['owner'] ?? null,
512-
)->get(function () use ($key, $callback, $created, $ttl) {
513-
if ($created !== $this->get("illuminate:cache:flexible:created:{$key}")) {
517+
)->get(function () use ($valueKey, $createdKey, $callback, $created, $ttl) {
518+
if ($created !== $this->get($createdKey)) {
514519
return;
515520
}
516521

517522
$this->putMany([
518-
$key => value($callback),
519-
"illuminate:cache:flexible:created:{$key}" => Carbon::now()->getTimestamp(),
523+
$valueKey => value($callback),
524+
$createdKey => Carbon::now()->getTimestamp(),
520525
], $ttl[1]);
521526
});
522527
};

0 commit comments

Comments
 (0)