Skip to content

Commit 0038ef2

Browse files
committed
#36337: Respect serialization and compression of phpredis during locking
- Support phpredis serialization when locking (none, php, json, igbinary, msgpack). - Support phpredis compression when locking (none, lzf, zstd, lz4).
1 parent b69db31 commit 0038ef2

File tree

3 files changed

+404
-1
lines changed

3 files changed

+404
-1
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
3+
namespace Illuminate\Cache;
4+
5+
use Illuminate\Redis\Connections\PhpRedisConnection;
6+
use Redis;
7+
use UnexpectedValueException;
8+
9+
class PhpRedisLock extends RedisLock
10+
{
11+
public function __construct(PhpRedisConnection $redis, string $name, int $seconds, ?string $owner = null)
12+
{
13+
parent::__construct($redis, $name, $seconds, $owner);
14+
}
15+
16+
/**
17+
* {@inheritDoc}
18+
*/
19+
public function release()
20+
{
21+
return (bool) $this->redis->eval(
22+
LuaScripts::releaseLock(),
23+
1,
24+
$this->name,
25+
$this->serializedAndCompressedOwner()
26+
);
27+
}
28+
29+
protected function serializedAndCompressedOwner(): string
30+
{
31+
$client = $this->redis->client();
32+
33+
/* If a serialization mode such as "php" or "igbinary" and/or a
34+
* compression mode such as "lzf" or "zstd" is enabled, the owner
35+
* must be serialized and/or compressed by us, because phpredis does
36+
* not do this for the eval command.
37+
*
38+
* Name must not be modified!
39+
*/
40+
$owner = $client->_serialize($this->owner);
41+
42+
/* Once the phpredis extension exposes a compress function like the
43+
* above `_serialize()` function, we should switch to it to guarantee
44+
* consistency in the way the extension serializes and compresses to
45+
* avoid the need to check each compression option ourselves.
46+
*
47+
* @see https://github.com/phpredis/phpredis/issues/1938
48+
*/
49+
if ($this->compressed()) {
50+
if ($this->lzfCompressed()) {
51+
$owner = \lzf_compress($owner);
52+
} elseif ($this->zstdCompressed()) {
53+
$owner = \zstd_compress($owner, $client->getOption(Redis::OPT_COMPRESSION_LEVEL));
54+
} elseif ($this->lz4Compressed()) {
55+
$owner = \lz4_compress($owner, $client->getOption(Redis::OPT_COMPRESSION_LEVEL));
56+
} else {
57+
throw new UnexpectedValueException(sprintf(
58+
'Unknown phpredis compression in use (%d). Unable to release lock.',
59+
$client->getOption(Redis::OPT_COMPRESSION)
60+
));
61+
}
62+
}
63+
64+
return $owner;
65+
}
66+
67+
protected function compressed(): bool
68+
{
69+
return $this->redis->client()->getOption(Redis::OPT_COMPRESSION) !== Redis::COMPRESSION_NONE;
70+
}
71+
72+
protected function lzfCompressed(): bool
73+
{
74+
return defined('Redis::COMPRESSION_LZF') &&
75+
$this->redis->client()->getOption(Redis::OPT_COMPRESSION) === Redis::COMPRESSION_LZF;
76+
}
77+
78+
protected function zstdCompressed(): bool
79+
{
80+
return defined('Redis::COMPRESSION_ZSTD') &&
81+
$this->redis->client()->getOption(Redis::OPT_COMPRESSION) === Redis::COMPRESSION_ZSTD;
82+
}
83+
84+
protected function lz4Compressed(): bool
85+
{
86+
return defined('Redis::COMPRESSION_LZ4') &&
87+
$this->redis->client()->getOption(Redis::OPT_COMPRESSION) === Redis::COMPRESSION_LZ4;
88+
}
89+
}

src/Illuminate/Cache/RedisStore.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Illuminate\Contracts\Cache\LockProvider;
66
use Illuminate\Contracts\Redis\Factory as Redis;
7+
use Illuminate\Redis\Connections\PhpRedisConnection;
78

89
class RedisStore extends TaggableStore implements LockProvider
910
{
@@ -188,7 +189,14 @@ public function forever($key, $value)
188189
*/
189190
public function lock($name, $seconds = 0, $owner = null)
190191
{
191-
return new RedisLock($this->lockConnection(), $this->prefix.$name, $seconds, $owner);
192+
$lockName = $this->prefix.$name;
193+
$lockConnection = $this->lockConnection();
194+
195+
if ($lockConnection instanceof PhpRedisConnection) {
196+
return new PhpRedisLock($lockConnection, $lockName, $seconds, $owner);
197+
}
198+
199+
return new RedisLock($lockConnection, $lockName, $seconds, $owner);
192200
}
193201

194202
/**

0 commit comments

Comments
 (0)