Skip to content

Commit f349abb

Browse files
Avoid writing multiple keys when using redis in cluster mode (#53940)
* Avoid writing multiple keys when using redis in cluster mode * formatting --------- Co-authored-by: Taylor Otwell <[email protected]>
1 parent 70607c5 commit f349abb

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed

src/Illuminate/Cache/RedisStore.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,19 @@
44

55
use Illuminate\Contracts\Cache\LockProvider;
66
use Illuminate\Contracts\Redis\Factory as Redis;
7+
use Illuminate\Redis\Connections\PhpRedisClusterConnection;
78
use Illuminate\Redis\Connections\PhpRedisConnection;
9+
use Illuminate\Redis\Connections\PredisClusterConnection;
810
use Illuminate\Redis\Connections\PredisConnection;
911
use Illuminate\Support\LazyCollection;
1012
use Illuminate\Support\Str;
1113

1214
class RedisStore extends TaggableStore implements LockProvider
1315
{
16+
use RetrievesMultipleKeys {
17+
putMany as private putManyAlias;
18+
}
19+
1420
/**
1521
* The Redis factory implementation.
1622
*
@@ -118,25 +124,33 @@ public function put($key, $value, $seconds)
118124
*/
119125
public function putMany(array $values, $seconds)
120126
{
127+
$connection = $this->connection();
128+
129+
// Cluster connections do not support writing multiple values if the keys hash differently...
130+
if ($connection instanceof PhpRedisClusterConnection ||
131+
$connection instanceof PredisClusterConnection) {
132+
return $this->putManyAlias($values, $seconds);
133+
}
134+
121135
$serializedValues = [];
122136

123137
foreach ($values as $key => $value) {
124138
$serializedValues[$this->prefix.$key] = $this->serialize($value);
125139
}
126140

127-
$this->connection()->multi();
141+
$connection->multi();
128142

129143
$manyResult = null;
130144

131145
foreach ($serializedValues as $key => $value) {
132-
$result = (bool) $this->connection()->setex(
146+
$result = (bool) $connection->setex(
133147
$key, (int) max(1, $seconds), $value
134148
);
135149

136150
$manyResult = is_null($manyResult) ? $result : $result && $manyResult;
137151
}
138152

139-
$this->connection()->exec();
153+
$connection->exec();
140154

141155
return $manyResult ?: false;
142156
}

tests/Integration/Cache/RedisStoreTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
namespace Illuminate\Tests\Integration\Cache;
44

55
use DateTime;
6+
use Illuminate\Cache\RedisStore;
67
use Illuminate\Foundation\Testing\Concerns\InteractsWithRedis;
8+
use Illuminate\Redis\Connections\PhpRedisClusterConnection;
79
use Illuminate\Support\Facades\Cache;
810
use Illuminate\Support\Facades\Redis;
911
use Illuminate\Support\Sleep;
12+
use Mockery as m;
1013
use Orchestra\Testbench\TestCase;
1114

1215
class RedisStoreTest extends TestCase
@@ -25,6 +28,7 @@ protected function tearDown(): void
2528
parent::tearDown();
2629

2730
$this->tearDownRedis();
31+
m::close();
2832
}
2933

3034
public function testCacheTtl(): void
@@ -231,4 +235,18 @@ public function testMultipleItemsCanBeSetAndRetrieved()
231235

232236
$this->assertEquals([], $store->many([]));
233237
}
238+
239+
public function testPutManyCallsPutWhenClustered()
240+
{
241+
$store = m::mock(RedisStore::class)->makePartial();
242+
$store->expects('connection')->andReturn(m::mock(PhpRedisClusterConnection::class));
243+
$store->expects('put')
244+
->twice()
245+
->andReturn(true);
246+
247+
$store->putMany([
248+
'foo' => 'bar',
249+
'fizz' => 'buz',
250+
], 10);
251+
}
234252
}

0 commit comments

Comments
 (0)