|
11 | 11 |
|
12 | 12 | namespace Symfony\Component\Lock\Tests\Store;
|
13 | 13 |
|
| 14 | +use Symfony\Component\Cache\Traits\RedisClusterProxy; |
| 15 | +use Symfony\Component\Cache\Traits\RedisProxy; |
| 16 | +use Symfony\Component\Lock\Exception\InvalidArgumentException; |
| 17 | +use Symfony\Component\Lock\Exception\LockConflictedException; |
| 18 | +use Symfony\Component\Lock\Key; |
14 | 19 | use Symfony\Component\Lock\PersistingStoreInterface;
|
15 | 20 | use Symfony\Component\Lock\Store\RedisStore;
|
16 | 21 |
|
@@ -43,4 +48,99 @@ public function getStore(): PersistingStoreInterface
|
43 | 48 | {
|
44 | 49 | return new RedisStore($this->getRedisConnection());
|
45 | 50 | }
|
| 51 | + |
| 52 | + public function testBackwardCompatibilityForward() |
| 53 | + { |
| 54 | + $resource = uniqid(__METHOD__, true); |
| 55 | + $key1 = new Key($resource); |
| 56 | + $key2 = new Key($resource); |
| 57 | + |
| 58 | + $oldStore = new Symfony51Store($this->getRedisConnection()); |
| 59 | + $newStore = $this->getStore(); |
| 60 | + |
| 61 | + $oldStore->save($key1); |
| 62 | + $this->assertTrue($oldStore->exists($key1)); |
| 63 | + |
| 64 | + $this->expectException(LockConflictedException::class); |
| 65 | + $newStore->save($key2); |
| 66 | + } |
| 67 | + |
| 68 | + public function testBackwardCompatibilityBackward() |
| 69 | + { |
| 70 | + $resource = uniqid(__METHOD__, true); |
| 71 | + $key1 = new Key($resource); |
| 72 | + $key2 = new Key($resource); |
| 73 | + |
| 74 | + $oldStore = new Symfony51Store($this->getRedisConnection()); |
| 75 | + $newStore = $this->getStore(); |
| 76 | + |
| 77 | + $newStore->save($key1); |
| 78 | + $this->assertTrue($newStore->exists($key1)); |
| 79 | + |
| 80 | + $this->expectException(LockConflictedException::class); |
| 81 | + $oldStore->save($key2); |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +class Symfony51Store |
| 86 | +{ |
| 87 | + private $redis; |
| 88 | + |
| 89 | + public function __construct($redis) |
| 90 | + { |
| 91 | + $this->redis = $redis; |
| 92 | + } |
| 93 | + |
| 94 | + public function save(Key $key) |
| 95 | + { |
| 96 | + $script = ' |
| 97 | + if redis.call("GET", KEYS[1]) == ARGV[1] then |
| 98 | + return redis.call("PEXPIRE", KEYS[1], ARGV[2]) |
| 99 | + elseif redis.call("SET", KEYS[1], ARGV[1], "NX", "PX", ARGV[2]) then |
| 100 | + return 1 |
| 101 | + else |
| 102 | + return 0 |
| 103 | + end |
| 104 | + '; |
| 105 | + if (!$this->evaluate($script, (string) $key, [$this->getUniqueToken($key), (int) ceil(5 * 1000)])) { |
| 106 | + throw new LockConflictedException(); |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + public function exists(Key $key) |
| 111 | + { |
| 112 | + return $this->redis->get((string) $key) === $this->getUniqueToken($key); |
| 113 | + } |
| 114 | + |
| 115 | + private function evaluate(string $script, string $resource, array $args) |
| 116 | + { |
| 117 | + if ( |
| 118 | + $this->redis instanceof \Redis || |
| 119 | + $this->redis instanceof \RedisCluster || |
| 120 | + $this->redis instanceof RedisProxy || |
| 121 | + $this->redis instanceof RedisClusterProxy |
| 122 | + ) { |
| 123 | + return $this->redis->eval($script, array_merge([$resource], $args), 1); |
| 124 | + } |
| 125 | + |
| 126 | + if ($this->redis instanceof \RedisArray) { |
| 127 | + return $this->redis->_instance($this->redis->_target($resource))->eval($script, array_merge([$resource], $args), 1); |
| 128 | + } |
| 129 | + |
| 130 | + if ($this->redis instanceof \Predis\ClientInterface) { |
| 131 | + return $this->redis->eval(...array_merge([$script, 1, $resource], $args)); |
| 132 | + } |
| 133 | + |
| 134 | + throw new InvalidArgumentException(sprintf('"%s()" expects being initialized with a Redis, RedisArray, RedisCluster or Predis\ClientInterface, "%s" given.', __METHOD__, get_debug_type($this->redis))); |
| 135 | + } |
| 136 | + |
| 137 | + private function getUniqueToken(Key $key): string |
| 138 | + { |
| 139 | + if (!$key->hasState(__CLASS__)) { |
| 140 | + $token = base64_encode(random_bytes(32)); |
| 141 | + $key->setState(__CLASS__, $token); |
| 142 | + } |
| 143 | + |
| 144 | + return $key->getState(__CLASS__); |
| 145 | + } |
46 | 146 | }
|
0 commit comments