class WithAdvisoryLock::Base

Attributes

connection[R]
lock_name[R]
shared[R]
timeout_seconds[R]
transaction[R]

Public Class Methods

lock_stack() click to toggle source
# File lib/with_advisory_lock/base.rb, line 43
def self.lock_stack
  # access doesn't need to be synchronized as it is only accessed by the current thread.
  Thread.current[:with_advisory_lock_stack] ||= []
end
new(connection, lock_name, options) click to toggle source
# File lib/with_advisory_lock/base.rb, line 24
def initialize(connection, lock_name, options)
  options = { timeout_seconds: options } unless options.respond_to?(:fetch)
  options.assert_valid_keys :timeout_seconds, :shared, :transaction

  @connection = connection
  @lock_name = lock_name
  @timeout_seconds = options.fetch(:timeout_seconds, nil)
  @shared = options.fetch(:shared, false)
  @transaction = options.fetch(:transaction, false)
end

Public Instance Methods

already_locked?() click to toggle source
# File lib/with_advisory_lock/base.rb, line 49
def already_locked?
  lock_stack.include? lock_stack_item
end
lock_stack_item() click to toggle source
# File lib/with_advisory_lock/base.rb, line 39
def lock_stack_item
  @lock_stack_item ||= LockStackItem.new(lock_str, shared)
end
lock_str() click to toggle source
# File lib/with_advisory_lock/base.rb, line 35
def lock_str
  @lock_str ||= "#{ENV['WITH_ADVISORY_LOCK_PREFIX']}#{lock_name}"
end
stable_hashcode(input) click to toggle source
# File lib/with_advisory_lock/base.rb, line 63
def stable_hashcode(input)
  if input.is_a? Numeric
    input.to_i
  else
    # Ruby MRI's String#hash is randomly seeded as of Ruby 1.9 so
    # make sure we use a deterministic hash.
    Zlib.crc32(input.to_s)
  end
end
unique_column_name() click to toggle source

Prevent AR from caching results improperly

# File lib/with_advisory_lock/base.rb, line 100
def unique_column_name
  "t#{SecureRandom.hex}"
end
with_advisory_lock_if_needed() { || ... } click to toggle source
# File lib/with_advisory_lock/base.rb, line 53
def with_advisory_lock_if_needed(&block)
  if already_locked?
    Result.new(true, yield)
  elsif timeout_seconds == 0
    yield_with_lock(&block)
  else
    yield_with_lock_and_timeout(&block)
  end
end
yield_with_lock() { |: nil| ... } click to toggle source
# File lib/with_advisory_lock/base.rb, line 84
def yield_with_lock
  if try_lock
    begin
      lock_stack.push(lock_stack_item)
      result = block_given? ? yield : nil
      Result.new(true, result)
    ensure
      lock_stack.pop
      release_lock
    end
  else
    FAILED_TO_LOCK
  end
end
yield_with_lock_and_timeout(&block) click to toggle source
# File lib/with_advisory_lock/base.rb, line 73
def yield_with_lock_and_timeout(&block)
  give_up_at = Time.now + @timeout_seconds if @timeout_seconds
  while @timeout_seconds.nil? || Time.now < give_up_at
    r = yield_with_lock(&block)
    return r if r.lock_was_acquired?
    # Randomizing sleep time may help reduce contention.
    sleep(rand(0.05..0.15))
  end
  FAILED_TO_LOCK
end