Redis: SCAN is not SCAN

In a Laravel application, it's crucial to always be aware of which Redis client you’re using. Different Redis clients (such as predis or phpredis) can have varying levels of performance, feature support, and configuration differences. Especially when it comes to the implementation of the SCAN command, there are differences that may not be immediately apparent.
A few days ago, we encountered a requirement to search for similarities among keys within a massive pool of keys. So far, no problem.
1$keys = Redis::keys($pattern);2 3foreach ($keys as $key) {4 Log::info($key);5}
Normally, this code would fulfill the requirement. The problem, however, is that the application for which we implemented this functionality handles several million requests per day, resulting in a massive set of caching keys in Redis.
The solution is SCAN
. Unlike KEYS
, which retrieves all keys at once, SCAN
iterates over the data, requiring a cursor to manage the results. But so far, this isn’t the issue.
Our next iteration was a classic attempt at iteration in Redis:
1$pattern = '/test/*' 2$cursor = '0'; 3$count = 50000; 4do { 5 $result = $redis->scan($cursor, ['match' => $pattern, 'count' => $count]); 6 if ($result === false) { 7 break; 8 } 9 10 $cursor = $result[0];11 $allResults = array_merge($allResults, $result[1]);12 13} while ($cursor !== '0');
An experienced eye will quickly notice that this implementation is primarily designed for PREDIS, a Redis client written in PHP.
Honestly, this caught us off guard. While our developers had been using Predis (perhaps out of convenience for setup 😉), the PHPRedis client, which is written in C, was set up in our staging environment.
The problem with this code is: It will not work with PHPRedis or at least it will not return any result. To change this you need to adjust the code a bit.
1$pattern = '/test/*' 2$cursor = config('database.redis.client') == 'predis' ? '0' : null; 3$count = 50000; 4do { 5 $result = $redis->scan($cursor, ['match' => $pattern, 'count' => $count]); 6 if ($result === false) { 7 break; 8 } 9 10 $cursor = $result[0];11 $allResults = array_merge($allResults, $result[1]);12 13} while ($cursor !== '0');
The result of this difference was absolutely absurd. Due to the slight variations in cursor handling between the clients, we ended up with no results at all.
So, always keep an eye on which Redis client you're using in Laravel! 🚀