Skip to content

Commit 74d5464

Browse files
dkuzmenchukDenis Kuzmenchuktaylorotwell
authored
[8.x] Fix possible out of memory error when deleting values by reference key from cache in Redis driver (#40039)
* [8.x] Fix possible out of memory error when deleting values by reference key from cache in Redis driver * formatting Co-authored-by: Denis Kuzmenchuk <[email protected]> Co-authored-by: Taylor Otwell <[email protected]>
1 parent c67acfd commit 74d5464

File tree

2 files changed

+21
-8
lines changed

2 files changed

+21
-8
lines changed

src/Illuminate/Cache/RedisTaggedCache.php

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,26 @@ protected function deleteKeysByReference($reference)
178178
*/
179179
protected function deleteValues($referenceKey)
180180
{
181-
$values = array_unique($this->store->connection()->smembers($referenceKey));
181+
$cursor = $defaultCursorValue = '0';
182+
183+
do {
184+
[$cursor, $valuesChunk] = $this->store->connection()->sscan(
185+
$referenceKey, $cursor, ['match' => '*', 'count' => 1000]
186+
);
187+
188+
// PhpRedis client returns false if set does not exist or empty. Array destruction
189+
// on false stores null in each variable. If valuesChunk is null, it means that
190+
// there were not results from the previously executed "sscan" Redis command.
191+
if (is_null($valuesChunk)) {
192+
break;
193+
}
194+
195+
$valuesChunk = array_unique($valuesChunk);
182196

183-
if (count($values) > 0) {
184-
foreach (array_chunk($values, 1000) as $valuesChunk) {
197+
if (count($valuesChunk) > 0) {
185198
$this->store->connection()->del(...$valuesChunk);
186199
}
187-
}
200+
} while (((string) $cursor) !== $defaultCursorValue);
188201
}
189202

190203
/**

tests/Cache/CacheTaggedCacheTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,16 +267,16 @@ public function testRedisCacheTagsCanBeFlushed()
267267
$store->shouldReceive('connection')->andReturn($conn = m::mock(stdClass::class));
268268

269269
// Forever tag keys
270-
$conn->shouldReceive('smembers')->once()->with('prefix:foo:forever_ref')->andReturn(['key1', 'key2']);
271-
$conn->shouldReceive('smembers')->once()->with('prefix:bar:forever_ref')->andReturn(['key3']);
270+
$conn->shouldReceive('sscan')->once()->with('prefix:foo:forever_ref', '0', ['match' => '*', 'count' => 1000])->andReturn(['0', ['key1', 'key2']]);
271+
$conn->shouldReceive('sscan')->once()->with('prefix:bar:forever_ref', '0', ['match' => '*', 'count' => 1000])->andReturn(['0', ['key3']]);
272272
$conn->shouldReceive('del')->once()->with('key1', 'key2');
273273
$conn->shouldReceive('del')->once()->with('key3');
274274
$conn->shouldReceive('del')->once()->with('prefix:foo:forever_ref');
275275
$conn->shouldReceive('del')->once()->with('prefix:bar:forever_ref');
276276

277277
// Standard tag keys
278-
$conn->shouldReceive('smembers')->once()->with('prefix:foo:standard_ref')->andReturn(['key4', 'key5']);
279-
$conn->shouldReceive('smembers')->once()->with('prefix:bar:standard_ref')->andReturn(['key6']);
278+
$conn->shouldReceive('sscan')->once()->with('prefix:foo:standard_ref', '0', ['match' => '*', 'count' => 1000])->andReturn(['0', ['key4', 'key5']]);
279+
$conn->shouldReceive('sscan')->once()->with('prefix:bar:standard_ref', '0', ['match' => '*', 'count' => 1000])->andReturn(['0', ['key6']]);
280280
$conn->shouldReceive('del')->once()->with('key4', 'key5');
281281
$conn->shouldReceive('del')->once()->with('key6');
282282
$conn->shouldReceive('del')->once()->with('prefix:foo:standard_ref');

0 commit comments

Comments
 (0)