64 parent::__construct( $config );
66 $this->lockServers = $config[
'lockServers'];
67 if ( isset( $config[
'srvsByBucket'] ) ) {
69 $this->srvsByBucket = array_filter( $config[
'srvsByBucket'],
'is_array' );
70 $this->srvsByBucket = array_values( $this->srvsByBucket );
72 $this->srvsByBucket = [ array_keys( $this->lockServers ) ];
75 $config[
'redisConfig'][
'serializer'] =
'none';
76 $this->redisPool = RedisConnectionPool::singleton( $config[
'redisConfig'] );
80 $status = StatusValue::newGood();
82 $pathList = array_merge( ...array_values( $pathsByType ) );
84 $server = $this->lockServers[$lockSrv];
85 $conn = $this->redisPool->getConnection( $server, $this->logger );
87 foreach ( $pathList as
$path ) {
88 $status->fatal(
'lockmanager-fail-acquirelock',
$path );
95 foreach ( $pathsByType as $type => $paths ) {
97 foreach ( $paths as
$path ) {
107 -- Load input params (e.g. session, ttl, time of request)
108 local rSession, rTTL, rMaxTTL, rTime = unpack(ARGV)
109 -- Check that all the locks can be acquired
110 for i,requestKey in ipairs(KEYS)
do
111 local _, _, rType, resourceKey =
string.find(requestKey,
"(%w+):(%w+)$")
112 local keyIsFree =
true
113 local currentLocks = redis.call(
'hKeys',resourceKey)
114 for i,lockKey in ipairs(currentLocks)
do
115 -- Get the type and session of
this lock
116 local _, _, type, session =
string.find(lockKey,
"(%w+):(%w+)")
117 -- Check any locks that are not owned by
this session
118 if session ~= rSession then
119 local lockExpiry = redis.call(
'hGet',resourceKey,lockKey)
120 if 1*lockExpiry < 1*rTime then
121 -- Lock is stale, so just prune it out
122 redis.call(
'hDel',resourceKey,lockKey)
123 elseif rType ==
'EX' or type ==
'EX' then
129 if not keyIsFree then
130 failed[#failed+1] = requestKey
133 -- If all locks could be acquired, then
do so
135 for i,requestKey in ipairs(KEYS)
do
136 local _, _, rType, resourceKey =
string.find(requestKey,
"(%w+):(%w+)$")
137 redis.call(
'hSet',resourceKey,rType ..
':' .. rSession,rTime + rTTL)
138 -- In addition to invalidation logic, be sure to garbage collect
139 redis.call(
'expire',resourceKey,rMaxTTL)
144 $res = $conn->luaEval( $script,
146 array_keys( $pathsByKey ),
154 count( $pathsByKey ) # number of first argument(s) that are keys
156 }
catch ( RedisException $e ) {
158 $this->redisPool->handleError( $conn, $e );
161 if ( $res ===
false ) {
162 foreach ( $pathList as
$path ) {
163 $status->fatal(
'lockmanager-fail-acquirelock',
$path );
165 } elseif ( count( $res ) ) {
166 $status->fatal(
'lockmanager-fail-conflict' );
173 $status = StatusValue::newGood();
175 $pathList = array_merge( ...array_values( $pathsByType ) );
177 $server = $this->lockServers[$lockSrv];
178 $conn = $this->redisPool->getConnection( $server, $this->logger );
180 foreach ( $pathList as
$path ) {
181 $status->fatal(
'lockmanager-fail-releaselock',
$path );
188 foreach ( $pathsByType as $type => $paths ) {
190 foreach ( $paths as
$path ) {
200 -- Load input params (e.g. session)
201 local rSession = unpack(ARGV)
202 for i,requestKey in ipairs(KEYS)
do
203 local _, _, rType, resourceKey =
string.find(requestKey,
"(%w+):(%w+)$")
204 local released = redis.call(
'hDel',resourceKey,rType ..
':' .. rSession)
206 -- Remove the whole structure
if it is now empty
207 if redis.call(
'hLen',resourceKey) == 0 then
208 redis.call(
'del',resourceKey)
211 failed[#failed+1] = requestKey
216 $res = $conn->luaEval( $script,
218 array_keys( $pathsByKey ),
223 count( $pathsByKey ) # number of first argument(s) that are keys
225 }
catch ( RedisException $e ) {
227 $this->redisPool->handleError( $conn, $e );
230 if ( $res ===
false ) {
231 foreach ( $pathList as
$path ) {
232 $status->fatal(
'lockmanager-fail-releaselock',
$path );
235 foreach ( $res as $key ) {
236 $status->fatal(
'lockmanager-fail-releaselock', $pathsByKey[$key] );