phpredis

Redis key restrictions with scan


I have some code that stores, in redis, a flag of whether a user is active, under a unique key per user.

class RedisProfileActiveRepo implements ProfileActiveRepo
{
    /** @var Redis  */
    private $redis;

    public function __construct(Redis $redis)
    {
        $this->redis = $redis;
    }

    public function markProfileIsActive(int $profile_id)
    {
        $keyname = ProfileIsActiveKey::getAbsoluteKeyName($profile_id);

        // Set the user specific key for 10 minutes
        $result = $this->redis->setex($keyname, 10 * 60, 'foobar');
    }

    public function getNumberOfActiveProfiles()
    {
        $count = 0;
        $pattern = ProfileIsActiveKey::getWildcardKeyName();
        $iterator = null;

        while (($keys = $this->redis->scan($iterator, $pattern)) !== false) {
            $count += count($keys);
        }

        return $count;
    }
}

When I generate the keys from this code:

namespace ProjectName;

class ProfileIsActive
{
    public static function getAbsoluteKeyName(int $profile_id) : string
    {
        return __CLASS__ . '_' . $profile_id;
    }

    public static function getWildcardKeyName() : string
    {
        return __CLASS__ . '_*';
    }
}

Which results in the keys looking like ProjectName\ProfileIsActive_1234 the scan command in Redis fails to match any keys.

When I replace the slashes with underscores:

class ProfileIsActive
{
    public static function getAbsoluteKeyName(int $profile_id) : string
    {
        return str_replace('\\', '', __CLASS__) . '_' . $profile_id;
    }

    public static function getWildcardKeyName() : string
    {
        return str_replace('\\', '', __CLASS__) . '_*';
    }
}

The code works as expected.

My question is - why is doing a scan with a slash in the keyname failing to behave as expected, and are there any other characters that should be avoided in keynames to avoid similar problems?


Solution

  • Theoretically latest Redis autoescapes backslashes when you set keys at redis-cli:

    127.0.0.1:6379> set this\test 1
    OK
    127.0.0.1:6379> keys this*
    1) "this\\test"
    

    Issue a MONITOR command in redis-cli before you run your php client code, and watch for SCAN commands. If your collection is big enough and your count parameter is absent or low enough you might not get the record:

    127.0.0.1:6379> scan 0 match this*
    1) "73728"
    2) (empty list or set)
    
    127.0.0.1:6379> scan 0 match this* count 10000
    1) "87704"
    2) 1) "this\\test"