class GlobalLock::Lock

Attributes

config[R]

Public Class Methods

new(opts = {}) click to toggle source
# File lib/global_lock/lock.rb, line 4
def initialize(opts = {})
  if opts.is_a?(GlobalLock::Config)
    @config = opts
  else
    @config = GlobalLock::Config.new(opts)
  end
end

Public Instance Methods

correct_key?(name, possible_key) click to toggle source
# File lib/global_lock/lock.rb, line 68
def correct_key?(name, possible_key)
  return false unless name && possible_key && !name.empty? && !possible_key.empty?

  actual_key = fetch_lock_key(name)
  possible_key == actual_key
end
lock(name, opts={}) click to toggle source
# File lib/global_lock/lock.rb, line 35
def lock(name, opts={})
  opts = config.lock_opts.merge(opts)
  ttl, retry_time, backoff_time = opts.values_at(:ttl, :retry_time, :backoff_time)

  key = SecureRandom.uuid
  success = write_lock(name, key, ex: ttl)

  if success
    key
  elsif retry_time > 0
    sleep backoff_time

    # Note: really, the backoff factor should be multiplied by retry_wait

    lock(
      name,
      ttl: ttl,
      retry_time: retry_time - backoff_time,
      backoff_time: backoff_time * 2 * rand(0.5..1.5)
    )
  else
    false
  end
end
unlock(name, key) click to toggle source
# File lib/global_lock/lock.rb, line 60
def unlock(name, key)
  if correct_key?(name, key)
    delete_lock(name)
  else
    false
  end
end
with_lock(name, existing_key=nil, opts={}, &block) click to toggle source
# File lib/global_lock/lock.rb, line 12
def with_lock(name, existing_key=nil, opts={}, &block)
  opts = config.lock_opts.merge(opts)
  raise ArgumentError.new("Block required") unless block_given?

  ret_val = nil

  if !existing_key.nil && correct_key?(name, existing_key)
    ret_val = block.call(existing_key)
  elsif !existing_key.nil?
    raise GlobalLock::Errors::FailedToLockError.new("Used incorrect existing key")
  else
    key = lock(name, opts)
    raise GlobalLock::Errors::FailedToLockError.new("Failed to acquire lock") if (key == false)

    ret_val = block.call(key)

    unlock_success = unlock(name, key)
    raise GlobalLock::Errors::FailedToUnlockError.new("Failed to unlock") unless unlock_success
  end

  ret_val
end

Protected Instance Methods

delete_lock(name) click to toggle source
# File lib/global_lock/lock.rb, line 93
def delete_lock(name)
  res = config.with_redis do |redis|
    redis.del(config.redis_prefix + name)
  end
  res == 1
end
fetch_lock_key(name) click to toggle source
# File lib/global_lock/lock.rb, line 87
def fetch_lock_key(name)
  config.with_redis do |redis|
    redis.get(config.redis_prefix + name)
  end
end
write_lock(name, key, ex: nil) click to toggle source
# File lib/global_lock/lock.rb, line 77
def write_lock(name, key, ex: nil)
  ex ||= config.default_ttl
  raise ArgumentError.new("Cannot write_lock with blank name") if name.empty?

  res = config.with_redis do |redis|
    redis.set(config.redis_prefix + name, key, ex: ex, nx: true)
  end
  res == true
end