class TransactionalLock::AdvisoryLock

Represents a database advisory lock. N.B. currently quite MySQL specific, except that its interface allows more than one active lock MySQL will only ever support one lock at a time (releasing earlier locks implicitly)

Attributes

name[R]
timeout[R]

Public Class Methods

acquired_locks() click to toggle source
# File lib/transactional_lock/advisory_lock.rb, line 18
def acquired_locks
  acquired_locks_changeable.dup.freeze
end
delete_lock(lock) click to toggle source
# File lib/transactional_lock/advisory_lock.rb, line 32
def delete_lock(lock)
  acquired_locks_changeable.delete(lock)
end
forget_locks!() click to toggle source
# File lib/transactional_lock/advisory_lock.rb, line 36
def forget_locks!
  @acquired_locks = []
end
new(name, timeout: ::TransactionalLock::Configuration.default_timeout) click to toggle source
# File lib/transactional_lock/advisory_lock.rb, line 49
def initialize(name, timeout: ::TransactionalLock::Configuration.default_timeout)
  @name = name
  @timeout = timeout
  @acquired = false
end
push_lock(lock) click to toggle source
# File lib/transactional_lock/advisory_lock.rb, line 28
def push_lock(lock)
  acquired_locks_changeable << lock
end
release_all_locks() click to toggle source
# File lib/transactional_lock/advisory_lock.rb, line 22
def release_all_locks
  acquired_locks.each do |lock|
    lock.release
  end
end

Private Class Methods

acquired_locks_changeable() click to toggle source
# File lib/transactional_lock/advisory_lock.rb, line 42
def acquired_locks_changeable
  @acquired_locks ||= []
end

Public Instance Methods

acquire() click to toggle source
# File lib/transactional_lock/advisory_lock.rb, line 55
def acquire
  return if already_locked?
  raise_on_lock_conflicts!

  result = ActiveRecord::Base.connection.execute(
             "SELECT GET_LOCK('#{sql_name}', #{sql_timeout})")

  unless result.first.first == 1
    raise LockAcquireError.new "Could not acquire lock '#{@name}'."
  end

  self.class.push_lock(self)
end
release() click to toggle source
# File lib/transactional_lock/advisory_lock.rb, line 69
def release
  ActiveRecord::Base.connection.execute("SELECT RELEASE_LOCK('#{sql_name}')")
ensure
  # In any case consider this lock being released (avoiding inability for new acquires)
  self.class.delete_lock(self)
end

Private Instance Methods

already_locked?() click to toggle source
# File lib/transactional_lock/advisory_lock.rb, line 78
def already_locked?
  self.class.acquired_locks.any? { |lock| lock.name == name }
end
raise_on_lock_conflicts!() click to toggle source
# File lib/transactional_lock/advisory_lock.rb, line 82
def raise_on_lock_conflicts!
  conflict_lock = self.class.acquired_locks.detect { |lock| lock.name != name }
  if conflict_lock
    raise LockConflict.new self, conflict_lock
  end
end
sql_name() click to toggle source
# File lib/transactional_lock/advisory_lock.rb, line 89
def sql_name
  ActiveRecord::Base.connection.quote_string(@name)
end
sql_timeout() click to toggle source
# File lib/transactional_lock/advisory_lock.rb, line 93
def sql_timeout
  Integer(@timeout)
end