Skip to content

Instantly share code, notes, and snippets.

@jippi
Created May 21, 2014 19:10
Show Gist options
  • Select an option

  • Save jippi/8bc58782c76c64f49f55 to your computer and use it in GitHub Desktop.

Select an option

Save jippi/8bc58782c76c64f49f55 to your computer and use it in GitHub Desktop.
<?php
/**
* Global Locking using Redis
*
*/
class RedisLock {
/**
* How long should the lock be kept active, even if the process dies
*
* @var integer
*/
public static $lockTimeout = 20;
/**
* Stores the expire time of the currently held lock
*
* @var int
*/
protected static $_expire;
/**
* Gets a lock or waits for it to become available
*
* @param mixed $key Item to lock
* @param int $timeout Time to wait for the key (seconds)
* @return mixed The key
* @throws LockException If the key is invalid
* @throws LockTimeoutException If the lock is not acquired before the method times out
*/
public static function get($key, $timeout = 10, $sleep = 1) {
if (empty($key)) {
throw new LockException('Invalid Key');
}
$start = time();
$acquired = false;
do {
static::$_expire = static::timeout();
if ($acquired = (static::_getRedis()->setnx("mutex:{$key}", static::$_expire))) {
break;
}
if ($acquired = (static::recover($key))) {
break;
}
if ($timeout === 0) {
break;
}
sleep($sleep);
} while(!is_numeric($timeout) || time() < $start + $timeout);
if (!$acquired) {
throw new LockTimeoutException("Timeout exceeded");
}
return $key;
}
/**
* Releases the lock
*
* @param mixed $key Item to lock
* @throws LockException If the key is invalid
*/
public static function release($key) {
if (empty($key)) {
throw new LockException('Invalid Key');
}
// Only release the lock if it hasn't expired
if (static::$_expire > time()) {
static::_getRedis()->del("mutex:{$key}");
}
}
/**
* Generates an expire time based on the current time
*
* @return int timeout
*/
protected static function timeout() {
return (int) (time() + static::$lockTimeout + 1);
}
/**
* Recover an abandoned lock
*
* @param mixed $key Item to lock
* @return bool Was the lock acquired?
*/
protected static function recover($key) {
if (($lockTimeout = static::_getRedis()->get("mutex:{$key}")) > time()) {
return false;
}
$timeout = static::timeout();
$currentTimeout = static::_getRedis()->getset("mutex:{$key}", $timeout);
if ($currentTimeout != $lockTimeout) {
return false;
}
static::$_expire = $timeout;
return true;
}
/**
* Get an redis instance
*
* @return Redis
*/
protected static function _getRedis() {
return ConnectionManager::getDataSource('redis');
}
}
class LockException extends RuntimeException {
}
class LockTimeoutException extends LockException {
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment