Skip to content

Commit b6c668c

Browse files
authored
Merge pull request #8952 from paulbalandan/redis-delete-matching-prefix
fix: `RedisHandler::deleteMatching()` not deleting matching keys if cache prefix is used
2 parents a13f54d + cb23bdb commit b6c668c

File tree

2 files changed

+49
-33
lines changed

2 files changed

+49
-33
lines changed

system/Cache/Handlers/RedisHandler.php

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,18 +175,17 @@ public function delete(string $key)
175175
*/
176176
public function deleteMatching(string $pattern)
177177
{
178+
/** @var list<string> $matchedKeys */
178179
$matchedKeys = [];
180+
$pattern = static::validateKey($pattern, $this->prefix);
179181
$iterator = null;
180182

181183
do {
182-
// Scan for some keys
184+
/** @var false|list<string>|Redis $keys */
183185
$keys = $this->redis->scan($iterator, $pattern);
184186

185-
// Redis may return empty results, so protect against that
186-
if ($keys !== false) {
187-
foreach ($keys as $key) {
188-
$matchedKeys[] = $key;
189-
}
187+
if (is_array($keys)) {
188+
$matchedKeys = [...$matchedKeys, ...$keys];
190189
}
191190
} while ($iterator > 0);
192191

tests/system/Cache/Handlers/RedisHandlerTest.php

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313

1414
namespace CodeIgniter\Cache\Handlers;
1515

16+
use CodeIgniter\Cache\CacheFactory;
1617
use CodeIgniter\CLI\CLI;
1718
use CodeIgniter\I18n\Time;
1819
use Config\Cache;
20+
use PHPUnit\Framework\Attributes\DataProvider;
1921
use PHPUnit\Framework\Attributes\Group;
2022

2123
/**
@@ -129,44 +131,59 @@ public function testDelete(): void
129131
$this->assertFalse($this->handler->delete(self::$dummy));
130132
}
131133

132-
public function testDeleteMatchingPrefix(): void
134+
#[DataProvider('provideDeleteMatching')]
135+
public function testDeleteMatching(string $pattern, int $expectedDeleteCount, string $prefix = ''): void
133136
{
134-
// Save 101 items to match on
137+
$cache = new Cache();
138+
139+
if ($prefix !== '') {
140+
$cache->prefix = $prefix;
141+
}
142+
143+
/** @var RedisHandler $handler */
144+
$handler = CacheFactory::getHandler($cache, 'redis');
145+
135146
for ($i = 1; $i <= 101; $i++) {
136-
$this->handler->save('key_' . $i, 'value' . $i);
147+
$handler->save('key_' . $i, 'value_' . $i);
137148
}
138149

139-
// check that there are 101 items is cache store
140-
$dbInfo = explode(',', $this->handler->getCacheInfo()['db0']);
141-
$this->assertSame('keys=101', $dbInfo[0]);
150+
$cacheInfo = $handler->getCacheInfo();
151+
$this->assertIsArray($cacheInfo);
152+
$this->assertArrayHasKey('db0', $cacheInfo);
153+
$this->assertIsString($cacheInfo['db0']);
154+
$this->assertMatchesRegularExpression('/^keys=(?P<count>\d+)/', $cacheInfo['db0']);
155+
156+
preg_match('/^keys=(?P<count>\d+)/', $cacheInfo['db0'], $matches);
157+
$this->assertSame(101, (int) $matches['count']);
158+
159+
$this->assertSame($expectedDeleteCount, $handler->deleteMatching($pattern));
142160

143-
// Checking that given the prefix "key_1", deleteMatching deletes 13 keys:
144-
// (key_1, key_10, key_11, key_12, key_13, key_14, key_15, key_16, key_17, key_18, key_19, key_100, key_101)
145-
$this->assertSame(13, $this->handler->deleteMatching('key_1*'));
161+
$cacheInfo = $handler->getCacheInfo();
162+
$this->assertIsArray($cacheInfo);
163+
$this->assertArrayHasKey('db0', $cacheInfo);
164+
$this->assertIsString($cacheInfo['db0']);
165+
$this->assertMatchesRegularExpression('/^keys=(?P<count>\d+)/', $cacheInfo['db0']);
146166

147-
// check that there remains (101 - 13) = 88 items is cache store
148-
$dbInfo = explode(',', $this->handler->getCacheInfo()['db0']);
149-
$this->assertSame('keys=88', $dbInfo[0]);
167+
preg_match('/^keys=(?P<count>\d+)/', $cacheInfo['db0'], $matches);
168+
$this->assertSame(101 - $expectedDeleteCount, (int) $matches['count']);
169+
170+
$handler->deleteMatching('key_*');
150171
}
151172

152-
public function testDeleteMatchingSuffix(): void
173+
/**
174+
* @return iterable<string, array{0: string, 1: int, 2?: string}>
175+
*/
176+
public static function provideDeleteMatching(): iterable
153177
{
154-
// Save 101 items to match on
155-
for ($i = 1; $i <= 101; $i++) {
156-
$this->handler->save('key_' . $i, 'value' . $i);
157-
}
158-
159-
// check that there are 101 items is cache store
160-
$dbInfo = explode(',', $this->handler->getCacheInfo()['db0']);
161-
$this->assertSame('keys=101', $dbInfo[0]);
178+
// Given the key "key_1*", deleteMatching() should delete 13 keys:
179+
// key_1, key_10, key_11, key_12, key_13, key_14, key_15, key_16, key_17, key_18, key_19, key_100, key_101
180+
yield 'prefix' => ['key_1*', 13];
162181

163-
// Checking that given the suffix "1", deleteMatching deletes 11 keys:
164-
// (key_1, key_11, key_21, key_31, key_41, key_51, key_61, key_71, key_81, key_91, key_101)
165-
$this->assertSame(11, $this->handler->deleteMatching('*1'));
182+
// Given the key "*1", deleteMatching() should delete 11 keys:
183+
// key_1, key_11, key_21, key_31, key_41, key_51, key_61, key_71, key_81, key_91, key_101
184+
yield 'suffix' => ['*1', 11];
166185

167-
// check that there remains (101 - 13) = 88 items is cache store
168-
$dbInfo = explode(',', $this->handler->getCacheInfo()['db0']);
169-
$this->assertSame('keys=90', $dbInfo[0]);
186+
yield 'cache-prefix' => ['key_1*', 13, 'foo_'];
170187
}
171188

172189
public function testIncrementAndDecrement(): void

0 commit comments

Comments
 (0)