54 parent::__construct( $config );
56 $this->lockServers = $config[
'lockServers'];
57 if ( isset( $config[
'srvsByBucket'] ) ) {
59 $this->srvsByBucket = array_filter( $config[
'srvsByBucket'],
'is_array' );
60 $this->srvsByBucket = array_values( $this->srvsByBucket );
62 $this->srvsByBucket = [ array_keys( $this->lockServers ) ];
65 $config[
'redisConfig'][
'serializer'] =
'none';
66 $this->redisPool = RedisConnectionPool::singleton( $config[
'redisConfig'] );
71 $status = StatusValue::newGood();
73 $pathList = array_merge( ...array_values( $pathsByType ) );
75 $server = $this->lockServers[$lockSrv];
76 $conn = $this->redisPool->getConnection( $server, $this->logger );
78 foreach ( $pathList as
$path ) {
79 $status->fatal(
'lockmanager-fail-acquirelock',
$path );
86 foreach ( $pathsByType as $type => $paths ) {
88 foreach ( $paths as
$path ) {
98 -- Load input params (e.g. session, ttl, time of request)
99 local rSession, rTTL, rMaxTTL, rTime = unpack(ARGV)
100 -- Check that all the locks can be acquired
101 for i,requestKey in ipairs(KEYS)
do
102 local _, _, rType, resourceKey =
string.find(requestKey,
"(%w+):(%w+)$")
103 local keyIsFree =
true
104 local currentLocks = redis.call(
'hKeys',resourceKey)
105 for i,lockKey in ipairs(currentLocks)
do
106 -- Get the type and session of
this lock
107 local _, _, type, session =
string.find(lockKey,
"(%w+):(%w+)")
108 -- Check any locks that are not owned by
this session
109 if session ~= rSession then
110 local lockExpiry = redis.call(
'hGet',resourceKey,lockKey)
111 if 1*lockExpiry < 1*rTime then
112 -- Lock is stale, so just prune it out
113 redis.call(
'hDel',resourceKey,lockKey)
114 elseif rType ==
'EX' or type ==
'EX' then
120 if not keyIsFree then
121 failed[#failed+1] = requestKey
124 -- If all locks could be acquired, then
do so
126 for i,requestKey in ipairs(KEYS)
do
127 local _, _, rType, resourceKey =
string.find(requestKey,
"(%w+):(%w+)$")
128 redis.call(
'hSet',resourceKey,rType ..
':' .. rSession,rTime + rTTL)
129 -- In addition to invalidation logic, be sure to garbage collect
130 redis.call(
'expire',resourceKey,rMaxTTL)
135 $res = $conn->luaEval( $script,
137 array_keys( $pathsByKey ),
145 count( $pathsByKey ) # number of first argument(s) that are keys
147 }
catch ( RedisException $e ) {
149 $this->redisPool->handleError( $conn, $e );
152 if ( $res ===
false ) {
153 foreach ( $pathList as
$path ) {
154 $status->fatal(
'lockmanager-fail-acquirelock',
$path );
156 } elseif ( count( $res ) ) {
157 $status->fatal(
'lockmanager-fail-conflict' );
165 $status = StatusValue::newGood();
167 $pathList = array_merge( ...array_values( $pathsByType ) );
169 $server = $this->lockServers[$lockSrv];
170 $conn = $this->redisPool->getConnection( $server, $this->logger );
172 foreach ( $pathList as
$path ) {
173 $status->fatal(
'lockmanager-fail-releaselock',
$path );
180 foreach ( $pathsByType as $type => $paths ) {
182 foreach ( $paths as
$path ) {
192 -- Load input params (e.g. session)
193 local rSession = unpack(ARGV)
194 for i,requestKey in ipairs(KEYS)
do
195 local _, _, rType, resourceKey =
string.find(requestKey,
"(%w+):(%w+)$")
196 local released = redis.call(
'hDel',resourceKey,rType ..
':' .. rSession)
198 -- Remove the whole structure
if it is now empty
199 if redis.call(
'hLen',resourceKey) == 0 then
200 redis.call(
'del',resourceKey)
203 failed[#failed+1] = requestKey
208 $res = $conn->luaEval( $script,
210 array_keys( $pathsByKey ),
215 count( $pathsByKey ) # number of first argument(s) that are keys
217 }
catch ( RedisException $e ) {
219 $this->redisPool->handleError( $conn, $e );
222 if ( $res ===
false ) {
223 foreach ( $pathList as
$path ) {
224 $status->fatal(
'lockmanager-fail-releaselock',
$path );
227 foreach ( $res as $key ) {
228 $status->fatal(
'lockmanager-fail-releaselock', $pathsByKey[$key] );