class Redis::Lock
Constants
- UNLOCK_LUA_SCRIPT
- VERSION
Attributes
Public Class Methods
@param redis is a Redis
instance @param key String for a unique name of the lock to acquire @param options Int for the max number of seconds a lock can be held before it is auto released @param options Int for the number of millis to sleep after the first time a lock is not acquired
(successive reattempts will be made with exponential back off)
# File lib/redis-lock.rb, line 20 def initialize(redis, key, options = {}) @redis = redis @key = "lock:#{key}" @auto_release_time = options[:auto_release_time] || 30 @base_sleep_in_secs = (options[:base_sleep] || 100) / 1000.0 # Unique token set as the redis value of @key when locked by this instance @instance_name = SecureRandom.hex # If lock was called and unlock has not yet been called, this is set to the time the lock was acquired @time_locked = nil end
Public Instance Methods
Acquire the lock. If a block is provided, the lock is acquired before yielding to the block and released once the block is returned. @param acquire_timeout Int for max number of seconds to spend acquiring the lock before raising an error
# File lib/redis-lock.rb, line 34 def lock(acquire_timeout = 10, &block) raise AcquireLockTimeOut.new unless attempt_lock(acquire_timeout) if block begin yield(self) ensure unlock end end end
@return Boolean that is true if the lock is currently held by any process
# File lib/redis-lock.rb, line 59 def locked? return !@redis.get(@key).nil? end
Determines whether or not the lock is held by this instance. By default, this method relies on the expiration time of the key as a performance optimization when possible. If this is undesirable for some reason, set force_remote to true. @param force_remote Boolean for whether to verify with a call to the redis server instead of using the lock time @return Boolean that is true if this lock instance currently holds the lock
# File lib/redis-lock.rb, line 68 def locked_by_me?(force_remote = false) if @time_locked if force_remote return @redis.get(@key) == @instance_name end if Time.now < @time_locked + @auto_release_time return true end end return false end
Releases the lock if it is held by this instance. By default, this method relies on the expiration time of the key as a performance optimization when possible. If this is undesirable for some reason, set force_remote to true. @param force_remote Boolean for whether to explicitly delete on the redis server instead of relying on expiration
# File lib/redis-lock.rb, line 48 def unlock(force_remote = false) # unlock is a no-op if we never called lock if @time_locked if Time.now < @time_locked + @auto_release_time || force_remote @redis.eval(UNLOCK_LUA_SCRIPT, [@key], [@instance_name]) end @time_locked = nil end end
Private Instance Methods
@param acquire_timeout Int for the number of seconds to spend attempting to acquire the lock @return true if locked, false otherwise
# File lib/redis-lock.rb, line 84 def attempt_lock(acquire_timeout) locked = false sleep_time = @base_sleep_in_secs when_to_timeout = Time.now + acquire_timeout until locked locked = @redis.set(@key, @instance_name, :nx => true, :ex => @auto_release_time) unless locked return false if Time.now > when_to_timeout sleep(sleep_time) # exponentially back off, but ensure that we take all of our wait time without going over sleep_time = [sleep_time * 2, when_to_timeout - Time.now].min end end @time_locked = Time.now return true end