Module: DoubleEntry::Locking

Includes:
Configurable
Defined in:
lib/double_entry/locking.rb

Overview

Lock financial accounts to ensure consistency.

In order to ensure financial transactions always keep track of balances consistently, database-level locking is needed. This module takes care of it.

See DoubleEntry.lock_accounts and DoubleEntry.transfer for the public interface to this stuff.

Locking is done on DoubleEntry::AccountBalance records. If an AccountBalance record for an account doesn't exist when you try to lock it, the locking code will create one.

script/jack_hammer can be used to run concurrency tests on double_entry to validates that locking works properly.

Defined Under Namespace

Classes: Configuration, Lock, LockDisaster, LockMustBeOutermostTransaction, LockNotHeld, LockWaitTimeout

Class Method Summary collapse

Methods included from Configurable

included

Class Method Details

.balance_for_locked_account(account) ⇒ Object

Return the account balance record for the given account name if there's a lock on it, or raise a LockNotHeld if there isn't.



55
56
57
# File 'lib/double_entry/locking.rb', line 55

def self.()
  Lock.new([]).balance_for()
end

.lock_accounts(*accounts) ⇒ Object

Run the passed in block in a transaction with the given accounts locked for update.

The transaction must be the outermost transaction to ensure data integrity. A LockMustBeOutermostTransaction will be raised if it isn't.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/double_entry/locking.rb', line 35

def self.lock_accounts(*accounts)
  lock = Lock.new(accounts)

  if lock.in_a_locked_transaction?
    lock.ensure_locked!
    yield
  else
    lock.perform_lock(&Proc.new)
  end

rescue ActiveRecord::StatementInvalid => exception
  if exception.message =~ /lock wait timeout/i
    raise LockWaitTimeout
  else
    raise
  end
end