class Sqeduler::RedisLock

Uses eval_sha to execute server-side scripts on redis. Avoids some of the potentially racey and brittle dependencies on Time-based redis locks in other locking libraries.

Constants

SLEEP_TIME

Attributes

key[R]
timeout[R]

Public Class Methods

new(key, options = {}) click to toggle source
# File lib/sqeduler/redis_lock.rb, line 13
def initialize(key, options = {})
  @key = key
  @expiration = options[:expiration]
  raise ArgumentError, "Expiration must be provided!" unless @expiration
  @timeout = options[:timeout] || 5
end
with_lock(key, options) { || ... } click to toggle source
# File lib/sqeduler/redis_lock.rb, line 66
def self.with_lock(key, options)
  raise "Block is required" unless block_given?
  mutex = new(key, options)
  unless mutex.lock
    raise LockTimeoutError, "Timed out trying to get #{key} lock. Exceeded #{mutex.timeout} sec"
  end
  begin
    yield
  ensure
    mutex.unlock
  end
end

Public Instance Methods

expiration_milliseconds() click to toggle source
# File lib/sqeduler/redis_lock.rb, line 79
def expiration_milliseconds
  # expiration needs to be an integer
  @expiration ? (@expiration * 1000).to_i : 0
end
lock() click to toggle source
# File lib/sqeduler/redis_lock.rb, line 20
def lock
  Service.logger.info(
    "Try to acquire lock with #{key}, expiration: #{@expiration} sec, timeout: #{timeout} sec"
  )
  return true if locked?
  if poll_for_lock
    Service.logger.info "Acquired lock #{key} with value #{lock_value}"
    true
  else
    Service.logger.info "Failed to acquire lock #{key} with value #{lock_value}"
    false
  end
end
locked?() click to toggle source
# File lib/sqeduler/redis_lock.rb, line 54
def locked?
  redis_pool.with do |redis|
    if redis.get(key) == lock_value
      Service.logger.info "Lock #{key} with value #{lock_value} is valid"
      true
    else
      Service.logger.info "Lock #{key} with value #{lock_value} has expired or is not present"
      false
    end
  end
end
refresh() click to toggle source
# File lib/sqeduler/redis_lock.rb, line 44
def refresh
  if refresh_lock
    Service.logger.info "Refreshed lock #{key} with value #{lock_value}"
    true
  else
    Service.logger.info "Cannot refresh lock #{key} with value #{lock_value}"
    false
  end
end
unlock() click to toggle source
# File lib/sqeduler/redis_lock.rb, line 34
def unlock
  if release_lock
    Service.logger.info "Released lock #{key} with value #{lock_value}"
    true
  else
    Service.logger.info "Cannot release lock #{key} with value #{lock_value}"
    false
  end
end

Private Instance Methods

lock_value() click to toggle source
# File lib/sqeduler/redis_lock.rb, line 86
def lock_value
  @lock_value ||= LockValue.new.value
end
poll_for_lock() click to toggle source
# File lib/sqeduler/redis_lock.rb, line 90
def poll_for_lock
  start = Time.now
  ran_at_least_once = false
  while Time.now - start < timeout || !ran_at_least_once
    locked = take_lock
    break if locked
    ran_at_least_once = true
    sleep SLEEP_TIME
  end
  locked
end
redis_pool() click to toggle source
# File lib/sqeduler/redis_lock.rb, line 108
def redis_pool
  Service.redis_pool
end
take_lock() click to toggle source
# File lib/sqeduler/redis_lock.rb, line 102
def take_lock
  redis_pool.with do |redis|
    redis.set(key, lock_value, :nx => true, :px => expiration_milliseconds)
  end
end