1

In my Laravel application, I used to use the predis/predis package to be able to use Redis for caching. Recently as recommended in the docs I have switched to PhpRedis but part of my code that was using SCAN is broken. In predis when I was using scan like this:

Redis::scan(0,'match',$pattern //Test:*);

I used to get a result like this:

[
     "253952",
     [
       "Test::296589",
       "Test::299112",
       "Test::332487",
       "Test::320358",
     ],
   ]

in this case 253952 was the next cursor that I could pass to scan again to get the next batch like this Redis::scan(253952,'match',$pattern //Test:*); and I could run this in a while loop until I reach the end:

$all_keys = [];
$keys = RedisManager::scan(0, 'match', $pattern);
while ($keys[0] !== "0") {
   foreach($keys[1] as $key) {
      $all_keys[] = $key;
   }
 $keys = RedisManager::scan($keys[0], 'match', $pattern);
}

Since I switched to Phpredis I can no longer iterate over the list. First of all the syntax seem to be different:

$it=null;
$keys = RedisManager::scan($it,$pattern);

I'm not sure what is the point of passing null as iterator to the first parameter. It also just returns:

[
 "Test::296589",
 "Test::299112"
]

which is just part of the result. It doesn't give me everything and it doesn't give me the next cursor. Can someone please guide me in the right direction?

Hirad Roshandel
  • 2,175
  • 5
  • 40
  • 63

1 Answers1

1

I switched to Phpredis on my local and made it work with the following snippet. The documentation states that there are different usages of the scan.

$redis = RedisManager::connection(); // initialize

$redis->setOption(\Redis::OPT_SCAN, \Redis::SCAN_RETRY); // please check documentation for other option
$keyList = [];
$iterator = null;

// you may update the count or match options according to your needs
while ($keys = $redis->scan($iterator, ['match' => 'Test:*', 'count' => 20])) {
    $iterator = $keys[0];
    foreach ($keys[1] as $key) {
        $keyList[] = $key;
    }
}

return $keyList;

While debugging or developing it would be useful to use monitor command. While that snippet is working it prints something like this;

1603241935.261667 [0 127.0.0.1:52536] "SCAN" "0" "COUNT" "20" "MATCH" "Test:*"
1603241935.262241 [0 127.0.0.1:52536] "SCAN" "48" "COUNT" "20" "MATCH" "Test:*"
1603241935.263421 [0 127.0.0.1:52536] "SCAN" "424" "COUNT" "20" "MATCH" "Test:*"
1603241935.264476 [0 127.0.0.1:52536] "SCAN" "260" "COUNT" "20" "MATCH" "Test:*"
1603241935.265508 [0 127.0.0.1:52536] "SCAN" "100" "COUNT" "20" "MATCH" "Test:*"
1603241935.266405 [0 127.0.0.1:52536] "SCAN" "460" "COUNT" "20" "MATCH" "Test:*"
1603241935.267137 [0 127.0.0.1:52536] "SCAN" "258" "COUNT" "20" "MATCH" "Test:*"
1603241935.268271 [0 127.0.0.1:52536] "SCAN" "402" "COUNT" "20" "MATCH" "Test:*"
1603241935.269344 [0 127.0.0.1:52536] "SCAN" "42" "COUNT" "20" "MATCH" "Test:*"
1603241935.270328 [0 127.0.0.1:52536] "SCAN" "198" "COUNT" "20" "MATCH" "Test:*"
1603241935.275193 [0 127.0.0.1:52536] "SCAN" "342" "COUNT" "20" "MATCH" "Test:*"
1603241935.276690 [0 127.0.0.1:52536] "SCAN" "286" "COUNT" "20" "MATCH" "Test:*"
1603241935.279386 [0 127.0.0.1:52536] "SCAN" "510" "COUNT" "20" "MATCH" "Test:*"
1603241935.281084 [0 127.0.0.1:52536] "SCAN" "81" "COUNT" "20" "MATCH" "Test:*"
1603241935.282568 [0 127.0.0.1:52536] "SCAN" "41" "COUNT" "20" "MATCH" "Test:*"
1603241935.284090 [0 127.0.0.1:52536] "SCAN" "185" "COUNT" "20" "MATCH" "Test:*"
1603241935.285949 [0 127.0.0.1:52536] "SCAN" "117" "COUNT" "20" "MATCH" "Test:*"
1603241935.288443 [0 127.0.0.1:52536] "SCAN" "253" "COUNT" "20" "MATCH" "Test:*"
1603241935.293034 [0 127.0.0.1:52536] "SCAN" "147" "COUNT" "20" "MATCH" "Test:*"
1603241935.294957 [0 127.0.0.1:52536] "SCAN" "155" "COUNT" "20" "MATCH" "Test:*"
1603241935.296113 [0 127.0.0.1:52536] "SCAN" "199" "COUNT" "20" "MATCH" "Test:*"
1603241935.296456 [0 127.0.0.1:52536] "SCAN" "247" "COUNT" "20" "MATCH" "Test:*"
1603241935.296723 [0 127.0.0.1:52536] "SCAN" "223" "COUNT" "20" "MATCH" "Test:*"
Ersoy
  • 8,816
  • 6
  • 34
  • 48
  • Thank you for your response. When I run you code I get this error `PHP Warning: Redis::scan() expects parameter 2 to be string, array given in /var/www/api/vendor/laravel/framework/src/Illuminate/Redis/Connections/Connection.php on line 114` – Hirad Roshandel Oct 21 '20 at 01:15
  • @HiradRoshandel probably different versions, please replace `scan($iterator, ['match' => 'Test:*', 'count' => 20]` with `scan($iterator, 'Test:*', 20)` https://prnt.sc/v3gkjc the documentation of the method in PhpRedisConnection class may give some hints. – Ersoy Oct 21 '20 at 01:20
  • `$keys = $redis->scan($iterator, 'Test:*', 20)` just returns an array of string `["Test::296589","Test::299112"]` which makes that forloop break. I wonder why we are getting different results. Your code is written for the way Predis outputs Scan. – Hirad Roshandel Oct 21 '20 at 01:24
  • forgot to mention I'm using laravel 5.8. Not sure if that makes a difference. In PhpRedisConnection.php I don't have any `scan` function – Hirad Roshandel Oct 21 '20 at 01:29
  • @HiradRoshandel Maybe you can try the example code in the documentation. https://github.com/phpredis/phpredis#example-27 - this code didn't work on my development environment (it doesn't re-assign the value of iterator(cause infinite loop), also returns a single array instead of multidimensional) but the key thing is setOption i think. – Ersoy Oct 21 '20 at 01:32
  • 1
    `$redis->setOption(\Redis::OPT_SCAN, \Redis::SCAN_RETRY);` is useful if you use a \Generator. – David DIVERRES Apr 17 '21 at 23:51