module Sequel::Postgres::PgAdvisoryLock

Constants

DEFAULT_LOCK_FUNCTION
LOCK_FUNCTIONS
SESSION_LEVEL_LOCKS
TRANSACTION_LEVEL_LOCKS
UNLOCK_FUNCTION

Public Instance Methods

advisory_lock_key_for(lock_name) click to toggle source
# File lib/sequel/extensions/pg_advisory_lock.rb, line 94
def advisory_lock_key_for(lock_name)
  Zlib.crc32(lock_name.to_s) % 2 ** 31
end
register_advisory_lock(name, lock_function = DEFAULT_LOCK_FUNCTION) click to toggle source

Beth: not sure how much extra value this registration provides. It turns the name into a number, and makes sure the name/number is unique, and that you don’t try and use a different lock function with the same name.

# File lib/sequel/extensions/pg_advisory_lock.rb, line 73
def register_advisory_lock(name, lock_function = DEFAULT_LOCK_FUNCTION)
  name = name.to_sym

  if registered_advisory_locks.key?(name) && registered_advisory_locks[name][:lock_function] != lock_function
    raise LockAlreadyRegistered, "Lock with name :#{name} is already registered with a different lock function (#{registered_advisory_locks[name][:lock_function]})"
  end

  key = advisory_lock_key_for(name)
  name_for_key = registered_advisory_locks.keys.find { |n| registered_advisory_locks[n].fetch(:key) == key }
  if name_for_key && name_for_key != name
    raise Error, "Lock key #{key} is already taken"
  end

  function = lock_function.to_sym
  unless LOCK_FUNCTIONS.include?(function)
    raise Error, "Invalid lock function :#{function}"
  end

  registered_advisory_locks[name] = { key: key, lock_function: function }
end
registered_advisory_locks() click to toggle source
# File lib/sequel/extensions/pg_advisory_lock.rb, line 32
def registered_advisory_locks
  @registered_advisory_locks ||= Concurrent::Hash.new
end
with_advisory_lock(name, id = nil) { || ... } click to toggle source
# File lib/sequel/extensions/pg_advisory_lock.rb, line 36
def with_advisory_lock(name, id = nil)
  options = registered_advisory_locks.fetch(name.to_sym)

  lock_key = options.fetch(:key)
  function_params = [lock_key, id].compact

  lock_function = options.fetch(:lock_function)
  transaction_level_lock = TRANSACTION_LEVEL_LOCKS.include?(lock_function)

  if transaction_level_lock
    # TODO: It's allowed to specify additional options (in particular, :server)
    #       while opening database transaction.
    #       That's why this check must be smarter.
    unless in_transaction?
      raise Error, "Transaction must be manually opened before using transaction level lock '#{lock_function}'"
    end

    if get(Sequel.function(lock_function, *function_params))
      yield
    end
  else
    synchronize do
      if get(Sequel.function(lock_function, *function_params))
        begin
          result = yield
        ensure
          get(Sequel.function(UNLOCK_FUNCTION, *function_params))
          result
        end
      end
    end
  end
end