-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathRedis.php
127 lines (107 loc) · 2.55 KB
/
Redis.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<?php
namespace LockManager\Driver;
/**
* Distributed Redis LockManager back-end driver.
*
* Algorithm original author: nickyleach
* Algorithm URL: https://gist.github.com/nickyleach/3694555
*
* (c) Valera Leontyev (feedbee), 2013
* All LockManager code published under BSD 3-Clause License http://choosealicense.com/licenses/bsd-3-clause/
*/
class Redis implements DriverInterface
{
/**
* int seconds or null
*/
const LOCK_ACQUIRE_TIMEOUT = 10;
/**
* int seconds
*/
const LOCK_EXPIRE_TIMEOUT = 20;
/**
* int microseconds
*/
const SLEEP = 100000; //@TODO: randomize it
/**
* string
*/
const KEY = 'LockManager';
/**
* @var \Redis $redis
*/
private $redis;
/**
* Stores the expire time of the currently held lock
* @var array
*/
private $expire = array();
public function __construct(\Redis $redis = null)
{
$this->setRedis($redis);
}
public function setRedis(\Redis $redis)
{
$this->redis = $redis;
}
public function getRedis()
{
return $this->redis;
}
public function doLock($key, $blockOnBusy)
{
$storageKey = self::KEY . ":{$key}";
$lockAcquireTimeout = $blockOnBusy ? self::LOCK_ACQUIRE_TIMEOUT : 0;
try {
$start = time();
do {
$this->expire[$key] = self::timeout();
if ($acquired = ($this->redis->setnx($storageKey, $this->expire[$key]))) break;
if ($acquired = ($this->recover($key))) break;
if ($lockAcquireTimeout === 0) break;
usleep(self::SLEEP);
} while (!is_numeric($lockAcquireTimeout) || time() < $start + $lockAcquireTimeout);
return $acquired;
} catch (\RedisException $e) {
return false;
}
}
public function doRelease($key)
{
$storageKey = self::KEY . ":{$key}";
try {
// Only release the lock if it hasn't expired
if($this->expire[$key] > time()) {
$this->redis->del($storageKey);
}
} catch (\RedisException $e) {
return false;
}
return true;
}
/**
* Generates an expire time based on the current time
* @return int timeout
*/
protected static function timeout() {
return (int) (time() + self::LOCK_EXPIRE_TIMEOUT + 1);
}
/**
* Recover an abandoned lock
* @param string $key Item to lock
* @return bool Was the lock acquired?
*/
protected function recover($key) {
$storageKey = self::KEY . ":{$key}";
if (($lockTimeout = $this->redis->get($storageKey)) > time()) {
return false;
}
$timeout = self::timeout();
$currentTimeout = $this->redis->getset($storageKey, $timeout);
if ($currentTimeout != $lockTimeout) {
return false;
}
$this->expire[$key] = $timeout;
return true;
}
}