Skip to content

Commit da484d3

Browse files
authored
Fix buffer overflow when flushing Redis cache tags with many keys (#57562)
1 parent a4623c2 commit da484d3

File tree

2 files changed

+36
-11
lines changed

2 files changed

+36
-11
lines changed

src/Illuminate/Cache/RedisTaggedCache.php

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -129,16 +129,11 @@ public function flush()
129129
$cachePrefix = $redisPrefix.$this->store->getPrefix();
130130

131131
$cacheTags = [];
132-
$keysToBeDeleted = [];
133132

134133
foreach ($this->tags->getNames() as $name) {
135134
$cacheTags[] = $cachePrefix.$this->tags->tagId($name);
136135
}
137136

138-
foreach ($this->tags->entries() as $entry) {
139-
$keysToBeDeleted[] = $this->store->getPrefix().$entry;
140-
}
141-
142137
$script = <<<'LUA'
143138
local prefix = table.remove(ARGV, 1)
144139
@@ -152,12 +147,18 @@ public function flush()
152147
end
153148
LUA;
154149

155-
$connection->eval(
156-
$script,
157-
count($keysToBeDeleted),
158-
...$keysToBeDeleted,
159-
...[$cachePrefix, ...$cacheTags]
160-
);
150+
$entries = $this->tags->entries()
151+
->map(fn (string $key) => $this->store->getPrefix().$key)
152+
->chunk(1000);
153+
154+
foreach ($entries as $keysToBeDeleted) {
155+
$connection->eval(
156+
$script,
157+
count($keysToBeDeleted),
158+
...$keysToBeDeleted,
159+
...[$cachePrefix, ...$cacheTags]
160+
);
161+
}
161162

162163
$this->event(new CacheFlushed($this->getName()));
163164

tests/Integration/Cache/RedisStoreTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,4 +267,28 @@ public function testIncrementWithSerializationEnabled()
267267
$store->increment('foo');
268268
$this->assertEquals(2, $store->get('foo'));
269269
}
270+
271+
public function testTagsCanBeFlushedWithLargeNumberOfKeys()
272+
{
273+
Cache::store('redis')->clear();
274+
275+
$tags = ['large-test-'.time()];
276+
277+
for ($i = 1; $i <= 5000; $i++) {
278+
Cache::store('redis')->tags($tags)->put("key:{$i}", "value:{$i}", 300);
279+
}
280+
281+
$this->assertEquals('value:1', Cache::store('redis')->tags($tags)->get('key:1'));
282+
$this->assertEquals('value:2500', Cache::store('redis')->tags($tags)->get('key:2500'));
283+
$this->assertEquals('value:5000', Cache::store('redis')->tags($tags)->get('key:5000'));
284+
285+
Cache::store('redis')->tags($tags)->flush();
286+
287+
$this->assertNull(Cache::store('redis')->tags($tags)->get('key:1'));
288+
$this->assertNull(Cache::store('redis')->tags($tags)->get('key:2500'));
289+
$this->assertNull(Cache::store('redis')->tags($tags)->get('key:5000'));
290+
291+
$keyCount = Cache::store('redis')->connection()->keys('*');
292+
$this->assertCount(0, $keyCount);
293+
}
270294
}

0 commit comments

Comments
 (0)