class RedisLock
Constants
- DEFAULT_ATTRS
Original defaults
- VERSION
Attributes
acquired_token[RW]
last_acquire_retries[RW]
Public Class Methods
acquire(opts={}, &block)
click to toggle source
Acquire a lock. Use options to override defaults. This method makes sure to release the lock as soon as the block is finalized.
# File lib/redis_lock.rb, line 37 def self.acquire(opts={}, &block) if block.arity != 1 raise ArgumentError.new('Expected lock parameter in block. Example: RedisLock.acquire(opts){|lock| do_stuff if lock.acquired? }') end lock = RedisLock.new(opts) if lock.acquire begin block.call(lock) # lock.acquired? => true ensure lock.release # Exception => release early end else block.call(lock) # lock.acquired? => false end end
configure() { |config| ... }
click to toggle source
Configure defaults
# File lib/redis_lock.rb, line 31 def self.configure yield @@config end
configure_restore_defaults()
click to toggle source
Restore original defaults
# File lib/redis_lock.rb, line 25 def self.configure_restore_defaults @@config = Struct.new(*DEFAULT_ATTRS.keys).new(*DEFAULT_ATTRS.values) end
new(opts={})
click to toggle source
# File lib/redis_lock.rb, line 53 def initialize(opts={}) # Check if options are valid allowed_opts = DEFAULT_ATTRS.keys invalid_opts = opts.keys - allowed_opts raise ArgumentError.new("Invalid options: #{invalid_opts.inspect}. Please use one of #{allowed_opts.inspect} ") unless invalid_opts.empty? # Set attributes from options or defaults self.redis = opts[:redis] || @@config.redis || Redis.new self.redis = Redis.new(redis) if redis.is_a? Hash # allow to use Redis options instead of a redis instance self.key = opts[:key] || @@config.key self.autorelease = opts[:autorelease] || @@config.autorelease self.retry = opts.include?(:retry) ? opts[:retry] : @@config.retry self.retry_timeout = opts[:retry_timeout] || @@config.retry_timeout self.retry_sleep = opts[:retry_sleep] || @@config.retry_sleep end
Public Instance Methods
acquire()
click to toggle source
Try to acquire the lock. Retrun true on success, false on failure (someone else has the lock)
# File lib/redis_lock.rb, line 72 def acquire @first_try_time ||= Time.now @token ||= SecureRandom.uuid # token is used to make sure that we own the lock when releasing it @retries ||= 0 # Lock using a redis key, if not exists (NX) with an expiration time (EX). # NOTE that the NX and EX options are not supported by REDIS versions older than 2.6.12 # See lock pattern: http://redis.io/commands/SET expire = (autorelease * 1000).to_i # to milliseconds if redis.set(key, @token, nx: true, px: expire) self.acquired_token = @token # assign acquired_token else self.acquired_token = nil # clear acquired_token, to make the acquired? method return false # Wait and try again if retry option is set and didn't timeout if self.retry and (Time.now - @first_try_time) < retry_timeout sleep retry_sleep # wait @retries += 1 return acquire # and try again end end self.last_acquire_retries = @retries @retries = nil # reset retries @first_try_time = nil # reset timestamp return self.acquired? end
acquired?()
click to toggle source
Check if last lock acquisition was successful. Note that it doesn't track autorelease, if the lock is naturally expired, this value will still be true.
# File lib/redis_lock.rb, line 128 def acquired? !!self.acquired_token # acquired_token is only set on success end
release()
click to toggle source
Release the lock. Returns a Symbol with the status of the operation:
* :success if properly released * :already_released if the lock was already released or expired (other process could be using it now) * :not_acquired if the lock was not acquired (no release action was made because it was not needed)
# File lib/redis_lock.rb, line 107 def release if acquired? if redis.respond_to? :eval # if eval command is available, run a lua script because is a faster way to remove the key script = 'if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return nil end' ret = redis.eval(script, [key], [self.acquired_token]) else # i.e. MockRedis doesn't have eval ret = if redis.get(key) == self.acquired_token then redis.del(key) else nil end end self.acquired_token = nil # cleanup acquired token if ret == nil :already_released else :success end else :not_acquired end end