class MasterLock::RedisLock
RedisLock
implements a mutex in Redis according to the strategy documented at redis.io/commands/SET#patterns. The lock has a string identifier and when acquired will be registered to an owner, also identified by a string. Locks have an expiration time, after which they will be released automatically so that unexpected failures do not result in locks getting stuck.
Constants
- DEFAULT_SLEEP_INTERVAL
Attributes
@return [String] the unique identifier for the locked resource
@return [String] the identity of the owner acquiring the lock
@return [Redis] the Redis connection used to manage lock
@return [Fixnum] the lifetime of the lock in seconds
Public Class Methods
# File lib/master_lock/redis_lock.rb, line 25 def initialize( redis:, key:, owner:, ttl:, sleep_interval: DEFAULT_SLEEP_INTERVAL ) @redis = redis @key = key @owner = owner @ttl = ttl @sleep_interval = sleep_interval end
Public Instance Methods
Attempt to acquire the lock. If the lock is already held, this will attempt multiple times to acquire the lock until the timeout period is up.
@param [Fixnum] how long to wait to acquire the lock before failing @return [Boolean] whether the lock was acquired successfully
# File lib/master_lock/redis_lock.rb, line 44 def acquire(timeout:) timeout_time = Time.now + timeout loop do locked = redis.set(redis_key, owner, nx: true, px: ttl_ms) return true if locked return false if Time.now >= timeout_time sleep(@sleep_interval) end end
Extend the expiration time of the lock if still held by this owner. If the lock is no longer held by the owner, this method will fail and return false. The lock lifetime is extended by the configured ttl.
@return [Boolean] whether the lock was extended successfully
# File lib/master_lock/redis_lock.rb, line 59 def extend result = eval_script( RedisScripts::EXTEND_SCRIPT, RedisScripts::EXTEND_SCRIPT_HASH, keys: [redis_key], argv: [owner, ttl_ms] ) result != 0 end
Release the lock if still held by this owner. If the lock is no longer held by the owner, this method will fail and return false.
@return [Boolean] whether the lock was released successfully
# File lib/master_lock/redis_lock.rb, line 73 def release result = eval_script( RedisScripts::RELEASE_SCRIPT, RedisScripts::RELEASE_SCRIPT_HASH, keys: [redis_key], argv: [owner] ) result != 0 end
Private Instance Methods
# File lib/master_lock/redis_lock.rb, line 89 def eval_script(script, script_hash, keys:, argv:) begin redis.evalsha(script_hash, keys: keys, argv: argv) rescue Redis::CommandError redis.eval(script, keys: keys, argv: argv) end end
# File lib/master_lock/redis_lock.rb, line 97 def redis_key "#{MasterLock.config.key_prefix}:#{key}" end
# File lib/master_lock/redis_lock.rb, line 85 def ttl_ms (ttl * 1000).to_i end