lib/good_job/lockable.rb in good_job-2.9.4 vs lib/good_job/lockable.rb in good_job-2.9.5
- old
+ new
@@ -146,10 +146,11 @@
# end
def with_advisory_lock(column: _advisory_lockable_column, function: advisory_lockable_function, unlock_session: false)
raise ArgumentError, "Must provide a block" unless block_given?
records = advisory_lock(column: column, function: function).to_a
+
begin
unscoped { yield(records) }
ensure
if unlock_session
advisory_unlock_session
@@ -159,10 +160,57 @@
end
end
end
end
+ # Acquires an advisory lock on this record if it is not already locked by
+ # another database session. Be careful to ensure you release the lock when
+ # you are done with {#advisory_unlock_key} to release all remaining locks.
+ # @param key [String, Symbol] Key to Advisory Lock against
+ # @param function [String, Symbol] Postgres Advisory Lock function name to use
+ # @return [Boolean] whether the lock was acquired.
+ def advisory_lock_key(key, function: advisory_lockable_function)
+ query = if function.include? "_try_"
+ <<~SQL.squish
+ SELECT #{function}(('x'||substr(md5($1::text), 1, 16))::bit(64)::bigint) AS locked
+ SQL
+ else
+ <<~SQL.squish
+ SELECT #{function}(('x'||substr(md5($1::text), 1, 16))::bit(64)::bigint)::text AS locked
+ SQL
+ end
+
+ binds = [
+ ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
+ ]
+ locked = connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Lock', binds).first['locked']
+ return locked unless block_given?
+ return nil unless locked
+
+ begin
+ yield
+ ensure
+ advisory_unlock_key(key, function: advisory_unlockable_function(function))
+ end
+ end
+
+ # Releases an advisory lock on this record if it is locked by this database
+ # session. Note that advisory locks stack, so you must call
+ # {#advisory_unlock} and {#advisory_lock} the same number of times.
+ # @param key [String, Symbol] Key to lock against
+ # @param function [String, Symbol] Postgres Advisory Lock function name to use
+ # @return [Boolean] whether the lock was released.
+ def advisory_unlock_key(key, function: advisory_unlockable_function)
+ query = <<~SQL.squish
+ SELECT #{function}(('x'||substr(md5($1::text), 1, 16))::bit(64)::bigint) AS unlocked
+ SQL
+ binds = [
+ ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
+ ]
+ connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Unlock', binds).first['unlocked']
+ end
+
def _advisory_lockable_column
advisory_lockable_column || primary_key
end
def supports_cte_materialization_specifiers?
@@ -203,52 +251,32 @@
# all remaining locks).
# @param key [String, Symbol] Key to Advisory Lock against
# @param function [String, Symbol] Postgres Advisory Lock function name to use
# @return [Boolean] whether the lock was acquired.
def advisory_lock(key: lockable_key, function: advisory_lockable_function)
- query = if function.include? "_try_"
- <<~SQL.squish
- SELECT #{function}(('x'||substr(md5($1::text), 1, 16))::bit(64)::bigint) AS locked
- SQL
- else
- <<~SQL.squish
- SELECT #{function}(('x'||substr(md5($1::text), 1, 16))::bit(64)::bigint)::text AS locked
- SQL
- end
-
- binds = [
- ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
- ]
- self.class.connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Lock', binds).first['locked']
+ self.class.advisory_lock_key(key, function: function)
end
# Releases an advisory lock on this record if it is locked by this database
# session. Note that advisory locks stack, so you must call
# {#advisory_unlock} and {#advisory_lock} the same number of times.
# @param key [String, Symbol] Key to lock against
# @param function [String, Symbol] Postgres Advisory Lock function name to use
# @return [Boolean] whether the lock was released.
def advisory_unlock(key: lockable_key, function: self.class.advisory_unlockable_function(advisory_lockable_function))
- query = <<~SQL.squish
- SELECT #{function}(('x'||substr(md5($1::text), 1, 16))::bit(64)::bigint) AS unlocked
- SQL
- binds = [
- ActiveRecord::Relation::QueryAttribute.new('key', key, ActiveRecord::Type::String.new),
- ]
- self.class.connection.exec_query(pg_or_jdbc_query(query), 'GoodJob::Lockable Advisory Unlock', binds).first['unlocked']
+ self.class.advisory_unlock_key(key, function: function)
end
# Acquires an advisory lock on this record or raises
# {RecordAlreadyAdvisoryLockedError} if it is already locked by another
# database session.
# @param key [String, Symbol] Key to lock against
# @param function [String, Symbol] Postgres Advisory Lock function name to use
# @raise [RecordAlreadyAdvisoryLockedError]
# @return [Boolean] +true+
def advisory_lock!(key: lockable_key, function: advisory_lockable_function)
- result = advisory_lock(key: key, function: function)
- result || raise(RecordAlreadyAdvisoryLockedError)
+ self.class.advisory_lock_key(key, function: function) || raise(RecordAlreadyAdvisoryLockedError)
end
# Acquires an advisory lock on this record and safely releases it after the
# passed block is completed. If the record is locked by another database
# session, this raises {RecordAlreadyAdvisoryLockedError}.
@@ -264,12 +292,14 @@
# end
def with_advisory_lock(key: lockable_key, function: advisory_lockable_function)
raise ArgumentError, "Must provide a block" unless block_given?
advisory_lock!(key: key, function: function)
- yield
- ensure
- advisory_unlock(key: key, function: self.class.advisory_unlockable_function(function)) unless $ERROR_INFO.is_a? RecordAlreadyAdvisoryLockedError
+ begin
+ yield
+ ensure
+ advisory_unlock(key: key, function: self.class.advisory_unlockable_function(function))
+ end
end
# Tests whether this record has an advisory lock on it.
# @param key [String, Symbol] Key to test lock against
# @return [Boolean]