Module: GoodJob::AdvisoryLockable

Extended by:
ActiveSupport::Concern
Included in:
BatchRecord, Job, Process
Defined in:
app/models/concerns/good_job/advisory_lockable.rb

Overview

Adds Postgres advisory locking capabilities to an ActiveRecord record. For details on advisory locks, see the Postgres documentation:

Examples:

Add this concern to a MyRecord class:

class MyRecord < ActiveRecord::Base
  include Lockable

  def my_method
    ...
  end
end

Defined Under Namespace

Classes: AdvisoryLockCounter

Constant Summary collapse

RecordAlreadyAdvisoryLockedError =

Indicates an advisory lock is already held on a record by another database session.

Class.new(StandardError)
ADVISORY_LOCK_COUNTS =
AdvisoryLockCounter.new

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.hash_functionObject



38
39
40
# File 'app/models/concerns/good_job/advisory_lockable.rb', line 38

def hash_function
  @_hash_function ||= "md5"
end

.hash_function=(value) ⇒ Object



34
35
36
# File 'app/models/concerns/good_job/advisory_lockable.rb', line 34

def hash_function=(value)
  @_hash_function = value
end

Instance Method Details

#advisory_lock(key: lockable_key, function: advisory_lockable_function, connection: nil, &block) ⇒ Boolean, Object

Acquires an advisory lock on this record if it is not already locked by another database session.

Without a block, acquires the lock and returns true/false. The caller is responsible for releasing with #advisory_unlock.

With a block, acquires the lock (raising RecordAlreadyAdvisoryLockedError if it cannot), yields, and releases the lock when the block completes.

Parameters:

  • key (String, Symbol) (defaults to: lockable_key)

    Key to Advisory Lock against

  • function (String, Symbol) (defaults to: advisory_lockable_function)

    Postgres Advisory Lock function name to use

Returns:

  • (Boolean, Object)

    whether the lock was acquired (no block), or the block result.



666
667
668
669
670
671
672
673
674
675
676
677
678
679
# File 'app/models/concerns/good_job/advisory_lockable.rb', line 666

def advisory_lock(key: lockable_key, function: advisory_lockable_function, connection: nil, &block)
  if block
    acquired = false
    result = self.class.advisory_lock_key(key, function: function, connection: connection) do
      acquired = true
      yield
    end
    raise RecordAlreadyAdvisoryLockedError unless acquired

    result
  else
    self.class.advisory_lock_key(key, function: function, connection: connection)
  end
end

#advisory_lock!(key: lockable_key, function: advisory_lockable_function, connection: nil) ⇒ Boolean

Acquires an advisory lock on this record or raises RecordAlreadyAdvisoryLockedError if it is already locked by another database session.

Parameters:

  • key (String, Symbol) (defaults to: lockable_key)

    Key to lock against

  • function (String, Symbol) (defaults to: advisory_lockable_function)

    Postgres Advisory Lock function name to use

Returns:

  • (Boolean)

    true

Raises:



698
699
700
# File 'app/models/concerns/good_job/advisory_lockable.rb', line 698

def advisory_lock!(key: lockable_key, function: advisory_lockable_function, connection: nil)
  advisory_lock(key: key, function: function, connection: connection) || raise(RecordAlreadyAdvisoryLockedError)
end

#advisory_locked?(key: lockable_key) ⇒ Boolean

Tests whether this record has an advisory lock on it.

Parameters:

  • key (String, Symbol) (defaults to: lockable_key)

    Key to test lock against

Returns:

  • (Boolean)


712
713
714
# File 'app/models/concerns/good_job/advisory_lockable.rb', line 712

def advisory_locked?(key: lockable_key)
  self.class.advisory_locked_key?(key)
end

#advisory_unlock(key: lockable_key, function: self.class.advisory_unlockable_function(advisory_lockable_function), connection: nil) ⇒ Boolean

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.

Parameters:

  • key (String, Symbol) (defaults to: lockable_key)

    Key to lock against

  • function (String, Symbol) (defaults to: self.class.advisory_unlockable_function(advisory_lockable_function))

    Postgres Advisory Lock function name to use

Returns:

  • (Boolean)

    whether the lock was released.



687
688
689
# File 'app/models/concerns/good_job/advisory_lockable.rb', line 687

def advisory_unlock(key: lockable_key, function: self.class.advisory_unlockable_function(advisory_lockable_function), connection: nil)
  self.class.advisory_unlock_key(key, function: function, connection: connection || self.class.lease_connection)
end

#advisory_unlock!(key: lockable_key, function: self.class.advisory_unlockable_function) ⇒ void

This method returns an undefined value.

Releases all advisory locks on the record that are held by the current database session.

Parameters:

  • key (String, Symbol) (defaults to: lockable_key)

    Key to lock against

  • function (String, Symbol) (defaults to: self.class.advisory_unlockable_function)

    Postgres Advisory Lock function name to use



735
736
737
# File 'app/models/concerns/good_job/advisory_lockable.rb', line 735

def advisory_unlock!(key: lockable_key, function: self.class.advisory_unlockable_function)
  self.class.advisory_unlock_key!(key, function: function)
end

#advisory_unlocked?(key: lockable_key) ⇒ Boolean

Tests whether this record does not have an advisory lock on it.

Parameters:

  • key (String, Symbol) (defaults to: lockable_key)

    Key to test lock against

Returns:

  • (Boolean)


719
720
721
# File 'app/models/concerns/good_job/advisory_lockable.rb', line 719

def advisory_unlocked?(key: lockable_key)
  !advisory_locked?(key: key)
end

#lockable_column_key(column: self.class._advisory_lockable_column) ⇒ String

Default Advisory Lock key for column-based locking

Returns:

  • (String)


747
748
749
# File 'app/models/concerns/good_job/advisory_lockable.rb', line 747

def lockable_column_key(column: self.class._advisory_lockable_column)
  "#{self.class.table_name}-#{self[column]}"
end

#lockable_keyString

Default Advisory Lock key

Returns:

  • (String)


741
742
743
# File 'app/models/concerns/good_job/advisory_lockable.rb', line 741

def lockable_key
  lockable_column_key
end

#owns_advisory_lock?(key: lockable_key) ⇒ Boolean

Tests whether this record is locked by the current database session.

Parameters:

  • key (String, Symbol) (defaults to: lockable_key)

    Key to test lock against

Returns:

  • (Boolean)


726
727
728
# File 'app/models/concerns/good_job/advisory_lockable.rb', line 726

def owns_advisory_lock?(key: lockable_key)
  self.class.owns_advisory_lock_key?(key)
end

#with_advisory_lock(key: lockable_key, function: advisory_lockable_function, &block) ⇒ Object

Deprecated.

Use #advisory_lock with a block instead.

Raises:

  • (ArgumentError)


703
704
705
706
707
# File 'app/models/concerns/good_job/advisory_lockable.rb', line 703

def with_advisory_lock(key: lockable_key, function: advisory_lockable_function, &block)
  raise ArgumentError, "Must provide a block" unless block

  advisory_lock(key: key, function: function, &block)
end