Skip to content

Commit 9c4ffff

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 9c4ffff

File tree

3 files changed

+435
-1
lines changed

3 files changed

+435
-1
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
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+
/**
12+
* Create a new phpredis lock instance.
13+
*
14+
* @param \Illuminate\Redis\Connections\PhpRedisConnection $redis
15+
* @param string $name
16+
* @param int $seconds
17+
* @param string|null $owner
18+
* @return void
19+
*/
20+
public function __construct(PhpRedisConnection $redis, string $name, int $seconds, ?string $owner = null)
21+
{
22+
parent::__construct($redis, $name, $seconds, $owner);
23+
}
24+
25+
/**
26+
* Release the lock.
27+
*
28+
* @return bool
29+
*/
30+
public function release()
31+
{
32+
return (bool) $this->redis->eval(
33+
LuaScripts::releaseLock(),
34+
1,
35+
$this->name,
36+
$this->serializedAndCompressedOwner()
37+
);
38+
}
39+
40+
protected function serializedAndCompressedOwner(): string
41+
{
42+
$client = $this->redis->client();
43+
44+
/* If a serialization mode such as "php" or "igbinary" and/or a
45+
* compression mode such as "lzf" or "zstd" is enabled, the owner
46+
* must be serialized and/or compressed by us, because phpredis does
47+
* not do this for the eval command.
48+
*
49+
* Name must not be modified!
50+
*/
51+
$owner = $client->_serialize($this->owner);
52+
53+
/* Once the phpredis extension exposes a compress function like the
54+
* above `_serialize()` function, we should switch to it to guarantee
55+
* consistency in the way the extension serializes and compresses to
56+
* avoid the need to check each compression option ourselves.
57+
*
58+
* @see https://github.com/phpredis/phpredis/issues/1938
59+
*/
60+
if ($this->compressed()) {
61+
if ($this->lzfCompressed()) {
62+
$owner = \lzf_compress($owner);
63+
} elseif ($this->zstdCompressed()) {
64+
$owner = \zstd_compress($owner, $client->getOption(Redis::OPT_COMPRESSION_LEVEL));
65+
} elseif ($this->lz4Compressed()) {
66+
$owner = \lz4_compress($owner, $client->getOption(Redis::OPT_COMPRESSION_LEVEL));
67+
} else {
68+
throw new UnexpectedValueException(sprintf(
69+
'Unknown phpredis compression in use (%d). Unable to release lock.',
70+
$client->getOption(Redis::OPT_COMPRESSION)
71+
));
72+
}
73+
}
74+
75+
return $owner;
76+
}
77+
78+
/**
79+
* Determines whether compression is enabled for the phpredis connection.
80+
*
81+
* @return bool
82+
*/
83+
protected function compressed()
84+
{
85+
return $this->redis->client()->getOption(Redis::OPT_COMPRESSION) !== Redis::COMPRESSION_NONE;
86+
}
87+
88+
/**
89+
* Determines whether lzf compression is enabled for the phpredis connection.
90+
*
91+
* @return bool
92+
*/
93+
protected function lzfCompressed()
94+
{
95+
return defined('Redis::COMPRESSION_LZF') &&
96+
$this->redis->client()->getOption(Redis::OPT_COMPRESSION) === Redis::COMPRESSION_LZF;
97+
}
98+
99+
/**
100+
* Determines whether zstd compression is enabled for the phpredis connection.
101+
*
102+
* @return bool
103+
*/
104+
protected function zstdCompressed()
105+
{
106+
return defined('Redis::COMPRESSION_ZSTD') &&
107+
$this->redis->client()->getOption(Redis::OPT_COMPRESSION) === Redis::COMPRESSION_ZSTD;
108+
}
109+
110+
/**
111+
* Determines whether lz4 compression is enabled for the phpredis connection.
112+
*
113+
* @return bool
114+
*/
115+
protected function lz4Compressed()
116+
{
117+
return defined('Redis::COMPRESSION_LZ4') &&
118+
$this->redis->client()->getOption(Redis::OPT_COMPRESSION) === Redis::COMPRESSION_LZ4;
119+
}
120+
}

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)