1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
|
# frozen_string_literal: true
module WithAdvisoryLock
class PostgreSQL < Base
# See http://www.postgresql.org/docs/9.1/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
def try_lock
pg_function = "pg_try_advisory#{transaction ? '_xact' : ''}_lock#{shared ? '_shared' : ''}"
execute_successful?(pg_function)
end
def release_lock
return if transaction
pg_function = "pg_advisory_unlock#{shared ? '_shared' : ''}"
execute_successful?(pg_function)
rescue ActiveRecord::StatementInvalid => e
raise unless e.message =~ / ERROR: +current transaction is aborted,/
begin
connection.rollback_db_transaction
execute_successful?(pg_function)
ensure
connection.begin_db_transaction
end
end
def execute_successful?(pg_function)
comment = lock_name.to_s.gsub(%r{(/\*)|(\*/)}, '--')
sql = "SELECT #{pg_function}(#{lock_keys.join(',')}) AS #{unique_column_name} /* #{comment} */"
result = connection.select_value(sql)
# MRI returns 't', jruby returns true. YAY!
['t', true].include?(result)
end
# PostgreSQL wants 2 32bit integers as the lock key.
def lock_keys
@lock_keys ||= [stable_hashcode(lock_name), ENV['WITH_ADVISORY_LOCK_PREFIX']].map do |ea|
# pg advisory args must be 31 bit ints
ea.to_i & 0x7fffffff
end
end
end
end
|