public function Redis_Lock_Backend_Predis::lockAcquire in Redis 7
Same name and namespace in other branches
- 7.2 lib/Redis/Lock/Backend/Predis.php \Redis_Lock_Backend_Predis::lockAcquire()
Acquire lock.
Parameters
string $name: Lock name.
float $timeout = 30.0: (optional) Lock lifetime in seconds.
Return value
bool
Overrides Redis_Lock_Backend_Interface::lockAcquire
File
- lib/
Redis/ Lock/ Backend/ Predis.php, line 8
Class
- Redis_Lock_Backend_Predis
- Predis lock backend implementation.
Code
public function lockAcquire($name, $timeout = 30.0) {
$client = Redis_Client::getClient();
$key = 'lock:' . $name;
$keyOwn = $key . ':owner';
$id = $this
->getLockId();
// Insure that the timeout is at least 1 second, we cannot do otherwise with
// Redis, this is a minor change to the function signature, but in real life
// nobody will notice with so short duration.
$timeout = ceil(max($timeout, 1));
// If we already have the lock, check for his owner and attempt a new EXPIRE
// command on it.
if (isset($this->_locks[$name])) {
// Create a new transaction, for atomicity.
$client
->watch($keyOwn);
// Global tells us we are the owner, but in real life it could have expired
// and another process could have taken it, check that.
if ($client
->get($keyOwn) != $id) {
// Explicit UNWATCH we are not going to run the MULTI/EXEC block.
$client
->unwatch($keyOwn);
unset($this->_locks[$name]);
return FALSE;
}
$replies = $client
->pipeline(function ($pipe) use ($key, $keyOwn, $timeout, $id) {
$pipe
->multi();
// See comment below, there is no guarantee that the lock does not
// expire while we are actually sending the transaction. At least it
// really narrow down the actual potential race condition range, but
// do not remove it.
$pipe
->expire($key, $timeout);
$pipe
->setex($keyOwn, $timeout, $id);
$pipe
->exec();
});
$execReply = array_pop($replies);
if (FALSE === $execReply[1]) {
unset($this->_locks[$name]);
return FALSE;
}
return TRUE;
}
else {
$client
->watch($key);
$replies = $client
->pipeline(function ($pipe) use ($key, $keyOwn, $timeout, $id) {
$pipe
->multi();
// The INCR command should reset the EXPIRE state, so we are now the
// official owner. Set the owner flag and real EXPIRE delay.
$pipe
->incr($key);
$pipe
->expire($key, $timeout);
$pipe
->setex($keyOwn, $timeout, $id);
$pipe
->exec();
});
$execReply = array_pop($replies);
// If another client modified the $key value, transaction will be discarded
// $result will be set to FALSE. This means atomicity have been broken and
// the other client took the lock instead of us. The another condition is
// the INCR result test. If we succeeded in incrementing the counter but
// that counter was more than 0, then someone else already have the lock
// case in which we cannot proceed.
// EXPIRE and SETEX won't return something here, EXEC return is index 2.
// This was determined debugging, seems to be Predis specific.
if (FALSE === $execReply[2] || 1 != $execReply[0]) {
return FALSE;
}
// Register the lock and return.
return $this->_locks[$name] = TRUE;
}
return FALSE;
}