62 parent::__construct( $config );
64 $this->lockServers = $config[
'lockServers'];
65 if ( isset( $config[
'srvsByBucket'] ) ) {
67 $this->srvsByBucket = array_filter( $config[
'srvsByBucket'],
'is_array' );
68 $this->srvsByBucket = array_values( $this->srvsByBucket );
70 $this->srvsByBucket = [ array_keys( $this->lockServers ) ];
73 $config[
'redisConfig'][
'serializer'] =
'none';
74 $this->redisPool = RedisConnectionPool::singleton( $config[
'redisConfig'] );
78 $status = StatusValue::newGood();
80 $pathList = array_merge( ...array_values( $pathsByType ) );
82 $server = $this->lockServers[$lockSrv];
83 $conn = $this->redisPool->getConnection( $server, $this->logger );
85 foreach ( $pathList as
$path ) {
86 $status->fatal(
'lockmanager-fail-acquirelock',
$path );
93 foreach ( $pathsByType as
$type => $paths ) {
95 foreach ( $paths as
$path ) {
105 -- Load input params (e.g. session, ttl, time of request)
106 local rSession, rTTL, rMaxTTL, rTime = unpack(ARGV)
107 -- Check that all the locks can be acquired
108 for i,requestKey in ipairs(KEYS)
do
109 local _, _, rType, resourceKey =
string.find(requestKey,
"(%w+):(%w+)$")
110 local keyIsFree =
true
111 local currentLocks = redis.call(
'hKeys',resourceKey)
112 for i,lockKey in ipairs(currentLocks)
do
113 -- Get the type and session of
this lock
114 local _, _, type, session =
string.find(lockKey,
"(%w+):(%w+)")
115 -- Check any locks that are not owned by
this session
116 if session ~= rSession then
117 local lockExpiry = redis.call(
'hGet',resourceKey,lockKey)
118 if 1*lockExpiry < 1*rTime then
119 -- Lock is stale, so just prune it out
120 redis.call(
'hDel',resourceKey,lockKey)
121 elseif rType ==
'EX' or type ==
'EX' then
127 if not keyIsFree then
128 failed[#failed+1] = requestKey
131 -- If all locks could be acquired, then
do so
133 for i,requestKey in ipairs(KEYS)
do
134 local _, _, rType, resourceKey =
string.find(requestKey,
"(%w+):(%w+)$")
135 redis.call(
'hSet',resourceKey,rType ..
':' .. rSession,rTime + rTTL)
136 -- In addition to invalidation logic, be sure to garbage collect
137 redis.call(
'expire',resourceKey,rMaxTTL)
142 $res = $conn->luaEval( $script,
144 array_keys( $pathsByKey ),
152 count( $pathsByKey ) # number of first argument(s) that are keys
154 }
catch ( RedisException $e ) {
156 $this->redisPool->handleError( $conn, $e );
159 if (
$res ===
false ) {
160 foreach ( $pathList as
$path ) {
161 $status->fatal(
'lockmanager-fail-acquirelock',
$path );
163 } elseif ( count(
$res ) ) {
164 $status->fatal(
'lockmanager-fail-conflict' );
171 $status = StatusValue::newGood();
173 $pathList = array_merge( ...array_values( $pathsByType ) );
175 $server = $this->lockServers[$lockSrv];
176 $conn = $this->redisPool->getConnection( $server, $this->logger );
178 foreach ( $pathList as
$path ) {
179 $status->fatal(
'lockmanager-fail-releaselock',
$path );
186 foreach ( $pathsByType as
$type => $paths ) {
188 foreach ( $paths as
$path ) {
198 -- Load input params (e.g. session)
199 local rSession = unpack(ARGV)
200 for i,requestKey in ipairs(KEYS)
do
201 local _, _, rType, resourceKey =
string.find(requestKey,
"(%w+):(%w+)$")
202 local released = redis.call(
'hDel',resourceKey,rType ..
':' .. rSession)
204 -- Remove the whole structure
if it is now empty
205 if redis.call(
'hLen',resourceKey) == 0 then
206 redis.call(
'del',resourceKey)
209 failed[#failed+1] = requestKey
214 $res = $conn->luaEval( $script,
216 array_keys( $pathsByKey ),
221 count( $pathsByKey ) # number of first argument(s) that are keys
223 }
catch ( RedisException $e ) {
225 $this->redisPool->handleError( $conn, $e );
228 if (
$res ===
false ) {
229 foreach ( $pathList as
$path ) {
230 $status->fatal(
'lockmanager-fail-releaselock',
$path );
233 foreach (
$res as $key ) {
234 $status->fatal(
'lockmanager-fail-releaselock', $pathsByKey[$key] );