class RedisLocker
Attributes
Public Class Methods
# File lib/redis-locker.rb, line 98 def self.logger @logger ||= Logger.new(STDOUT) end
# File lib/redis-locker.rb, line 102 def self.logger=(logger) @logger = logger end
@param [String] key @param [Integer] time_limit
Number of seconds when locker will be expired
# File lib/redis-locker.rb, line 11 def initialize(key, time_limit = 5) @key = key @time_limit = time_limit @running = false end
# File lib/redis-locker.rb, line 106 def self.redis @redis end
# File lib/redis-locker.rb, line 110 def self.redis=(adapter) @redis = adapter end
Public Instance Methods
@return [true, false]
# File lib/redis-locker.rb, line 18 def current? concurrent_timestamp == timestamp end
Puts running block information in Redis This information will be used to place running block in a specific position of its queue
# File lib/redis-locker.rb, line 24 def enter_queue logger.info("Entering #@key") raise 'This block is already in the queue' if running? @running = true self.timestamp = generate_timestamp.to_s redis.set timestamp_key, '1' redis.expire timestamp_key, time_limit redis.rpush key, timestamp end
Clears all data from queue related to this block
# File lib/redis-locker.rb, line 37 def exit_queue logger.info("Leaving #@key") redis.del timestamp_key redis.lrem key, 1, timestamp @running = false end
Returns true if block is ready to run @return [true, false]
# File lib/redis-locker.rb, line 46 def get_ready if ready? concurrent_timestamp.nil? ? start_queue : make_current true else current? end end
# File lib/redis-locker.rb, line 55 def ready? concurrent_timestamp.nil? || current? || (generate_timestamp - concurrent_timestamp.to_f >= time_limit) || redis.get(generate_timestamp_key(concurrent_timestamp)).nil? end
# File lib/redis-locker.rb, line 61 def redis self.class.redis end
Waits for the queue and evaluates the block
# File lib/redis-locker.rb, line 66 def run(&block) logger.info("Running queue #@key") enter_queue wait begin block.call ensure exit_queue end end
@param [Integer] time_limit
Number of seconds after we throw a Timeout::Error @param [true, false] clear_queue_on_timeout @raise [Timeout::Error]
# File lib/redis-locker.rb, line 81 def run!(time_limit = @time_limit, clear_queue_on_timeout = false, &block) Timeout::timeout(time_limit) { run(&block) } rescue Timeout::Error => error logger.error("Failed by timeout #{time_limit}s on #@key") if clear_queue_on_timeout logger.info("Clearing queue #@key") clear_queue end raise error end
# File lib/redis-locker.rb, line 94 def running? @running end
Protected Instance Methods
@return [Float]
# File lib/redis-locker.rb, line 117 def generate_timestamp Time.now.to_f end
Private Instance Methods
# File lib/redis-locker.rb, line 123 def clear_queue redis.del key end
@return [String]
# File lib/redis-locker.rb, line 128 def concurrent_timestamp @concurrent_timestamp ||= fetch_concurrent_timestamp end
Fetches next concurrent thread ID from the queue
# File lib/redis-locker.rb, line 133 def fetch_concurrent_timestamp redis.lindex(key, 0) end
@param [String, Float] timestamp
# File lib/redis-locker.rb, line 138 def generate_timestamp_key(timestamp = @timestamp) "Locker::__key_#{timestamp}" end
@return [Logger]
# File lib/redis-locker.rb, line 143 def logger self.class.logger end
Replaces concurrent timestamp
# File lib/redis-locker.rb, line 148 def make_current redis.lrem key, 0, timestamp redis.lpop key redis.lpush key, timestamp end
Builds queue starting from self
# File lib/redis-locker.rb, line 156 def start_queue redis.lpush key, timestamp end
@param [Float] value
# File lib/redis-locker.rb, line 161 def timestamp=(value) @timestamp = value @timestamp_key = generate_timestamp_key(@timestamp) @timestamp end
Locking itself
# File lib/redis-locker.rb, line 168 def wait begin @concurrent_timestamp = fetch_concurrent_timestamp end until get_ready end