Skip to content

Commit

Permalink
#36337: Respect serialization and compression of phpredis during locking
Browse files Browse the repository at this point in the history
- Support phpredis serialization when locking (none, php, json, igbinary, msgpack).
- Support phpredis compression when locking (none, lzf, zstd, lz4).
  • Loading branch information
TheLevti committed Feb 28, 2021
1 parent b69db31 commit 0038ef2
Show file tree
Hide file tree
Showing 3 changed files with 404 additions and 1 deletion.
89 changes: 89 additions & 0 deletions src/Illuminate/Cache/PhpRedisLock.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

namespace Illuminate\Cache;

use Illuminate\Redis\Connections\PhpRedisConnection;
use Redis;
use UnexpectedValueException;

class PhpRedisLock extends RedisLock
{
public function __construct(PhpRedisConnection $redis, string $name, int $seconds, ?string $owner = null)
{
parent::__construct($redis, $name, $seconds, $owner);
}

/**
* {@inheritDoc}
*/
public function release()
{
return (bool) $this->redis->eval(
LuaScripts::releaseLock(),
1,
$this->name,
$this->serializedAndCompressedOwner()
);
}

protected function serializedAndCompressedOwner(): string
{
$client = $this->redis->client();

/* If a serialization mode such as "php" or "igbinary" and/or a
* compression mode such as "lzf" or "zstd" is enabled, the owner
* must be serialized and/or compressed by us, because phpredis does
* not do this for the eval command.
*
* Name must not be modified!
*/
$owner = $client->_serialize($this->owner);

/* Once the phpredis extension exposes a compress function like the
* above `_serialize()` function, we should switch to it to guarantee
* consistency in the way the extension serializes and compresses to
* avoid the need to check each compression option ourselves.
*
* @see https://github.com/phpredis/phpredis/issues/1938
*/
if ($this->compressed()) {
if ($this->lzfCompressed()) {
$owner = \lzf_compress($owner);
} elseif ($this->zstdCompressed()) {
$owner = \zstd_compress($owner, $client->getOption(Redis::OPT_COMPRESSION_LEVEL));
} elseif ($this->lz4Compressed()) {
$owner = \lz4_compress($owner, $client->getOption(Redis::OPT_COMPRESSION_LEVEL));
} else {
throw new UnexpectedValueException(sprintf(
'Unknown phpredis compression in use (%d). Unable to release lock.',
$client->getOption(Redis::OPT_COMPRESSION)
));
}
}

return $owner;
}

protected function compressed(): bool
{
return $this->redis->client()->getOption(Redis::OPT_COMPRESSION) !== Redis::COMPRESSION_NONE;
}

protected function lzfCompressed(): bool
{
return defined('Redis::COMPRESSION_LZF') &&
$this->redis->client()->getOption(Redis::OPT_COMPRESSION) === Redis::COMPRESSION_LZF;
}

protected function zstdCompressed(): bool
{
return defined('Redis::COMPRESSION_ZSTD') &&
$this->redis->client()->getOption(Redis::OPT_COMPRESSION) === Redis::COMPRESSION_ZSTD;
}

protected function lz4Compressed(): bool
{
return defined('Redis::COMPRESSION_LZ4') &&
$this->redis->client()->getOption(Redis::OPT_COMPRESSION) === Redis::COMPRESSION_LZ4;
}
}
10 changes: 9 additions & 1 deletion src/Illuminate/Cache/RedisStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Contracts\Cache\LockProvider;
use Illuminate\Contracts\Redis\Factory as Redis;
use Illuminate\Redis\Connections\PhpRedisConnection;

class RedisStore extends TaggableStore implements LockProvider
{
Expand Down Expand Up @@ -188,7 +189,14 @@ public function forever($key, $value)
*/
public function lock($name, $seconds = 0, $owner = null)
{
return new RedisLock($this->lockConnection(), $this->prefix.$name, $seconds, $owner);
$lockName = $this->prefix.$name;
$lockConnection = $this->lockConnection();

if ($lockConnection instanceof PhpRedisConnection) {
return new PhpRedisLock($lockConnection, $lockName, $seconds, $owner);
}

return new RedisLock($lockConnection, $lockName, $seconds, $owner);
}

/**
Expand Down
Loading

0 comments on commit 0038ef2

Please sign in to comment.