class DBLock::Adapter::Postgres

Public Instance Methods

lock(name, timeout = 0) click to toggle source
# File lib/db_lock/adapter/postgres.rb, line 8
def lock(name, timeout = 0)
  pid = connection_pid(connection)
  Timeout.timeout(timeout, LockTimeout) do
    execute lock_query(name)
    # Sadly this returns void in postgres
    true
  end
rescue LockTimeout
  logger&.info 'DBLock: Recovering from expired lock query'
  recover_from_timeout pid, name
end
release(name) click to toggle source
# File lib/db_lock/adapter/postgres.rb, line 20
def release(name)
  res = select_value 'SELECT pg_advisory_unlock(hashtext(?))', name
  res == true
end

Private Instance Methods

connection_pid(con) click to toggle source
# File lib/db_lock/adapter/postgres.rb, line 31
def connection_pid(con)
  select_value 'SELECT pg_backend_pid()', connection: con
end
lock_query(name) click to toggle source
# File lib/db_lock/adapter/postgres.rb, line 27
def lock_query(name)
  sanitize_sql_array 'SELECT pg_advisory_lock(hashtext(?))', name
end
recover_from_timeout(pid, name) click to toggle source

We have to manually kill the lock query. Connection pool keeps it alive blocking one connection. Also it would eventually acquire the lock. returns true if lock was acquired

# File lib/db_lock/adapter/postgres.rb, line 39
      def recover_from_timeout(pid, name)
        with_dedicated_connection do |con|
          lock = select_one(<<~SQL, pid, name, connection: con)
            SELECT locktype, objid, pid, granted FROM pg_locks \
            WHERE pid = ? AND locktype = 'advisory' AND objid = hashtext(?)
          SQL
          return false unless lock

          if lock['granted']
            logger&.info 'DBLock: Lock was acquired after all'
            true
          else
            res = select_value 'SELECT pg_cancel_backend(?)', pid, connection: con
            logger&.warn 'DBLock: Failed to cancel ungranted lock query' unless res == true
            false
          end
        end
      end
with_dedicated_connection() { |con| ... } click to toggle source
# File lib/db_lock/adapter/postgres.rb, line 58
def with_dedicated_connection
  con = pool.checkout
  yield con
ensure
  pool.checkin con
end