Транзакция Rails не действует атомарно? - PullRequest
1 голос
/ 06 мая 2019

У меня есть система бухгалтерского учета, которую я написал, которая следует стандартным методам учета с двумя записями.

Существует функция двойного учета, которая называется «пробный баланс», где вы можете проверить правильность всей системы, потому что при запуске она всегда будет равна 0.00

Я написал тесты и всегда запускаю пробный баланс, когда система «остановлена», но при высокой нагрузке на базу данных во время заполнения большого количества записей я заметил, что мой пробный баланс НЕПРАВИЛЬНО составляет примерно 1 из 10 попыток.

Когда он в состоянии покоя (без вставок), он всегда корректен при 0.00.

Когда я вставляю транзакции, они всегда в транзакции, например:

2000.times do |i|
  ActiveRecord::Base.transaction do
    puts "#{i} ==================================================================="

    entry = JournalEntry.create!(description: 'Purchase mower on credit', user: user)
    entry.line_items.create!(amount: Money.from_amount(1551.75).cents, account: property.accounts.find_by(name: 'Equipment'), side: :debit)
    entry.line_items.create!(amount: Money.from_amount(1551.75).cents, account: property.accounts.find_by(name: 'Accounts Payable'), side: :credit)
  end
end

Тот факт, что он ломается под нагрузкой, заставляет меня думать, что я не понимаю что-то важное о том, как работают транзакции Rails ......

Что может быть причиной этого?

FWIW моя функция пробного баланса (GeneralLedger.new(property).trial_balance) выполняет следующий псевдо-SQL ( НЕ в транзакции):

SELECT sum(...) WHERE account = 'asset'
SELECT sum(...) WHERE account = 'liability'
SELECT sum(...) WHERE account = 'equity'
SELECT sum(...) WHERE account = 'income'
SELECT sum(...) WHERE account = 'expense'

Затем я складываю их вместе в соответствии с формулой учета, чтобы получить 0,00:

  def trial_balance
    balance_category(:asset) - (balance_category(:liability) + balance_category(:equity) + balance_category(:income) - balance_category(:expense))
  end

Функция balance_category - это то, что запускает каждый SELECT в общей сложности 5 раз, один раз для каждой категории.

Поскольку он возвращает 0, это означает, что он каким-то образом выбирает, когда он наполовину вставлен ........... Я понятия не имею, как это происходит?

Я мог бы понять, если создание записи / позиции в журнале не было в транзакции, и это было ВЫБОР наполовину вставленных строк, но он должен выбирать только из всей группы в целом после завершения транзакции?

1 Ответ

1 голос
/ 06 мая 2019

Если вы хотите избежать повторяющихся утверждений, сверните их в одно, что-то вроде этой формы:

SELECT account, SUM(...) AS amount FROM ...
   WHERE account IN ('asset', 'liability', ...)
   GROUP BY account

Вы можете получить их следующим образом:

where(account: ACCOUNT_TYPES).group(:account).pluck('account, SUM(...)')

Где ACCOUNT_TYPESэто массив типов учетных записей, которые вам нужно выбрать.

Вы всегда можете взять эту форму pluck и преобразовать ее в быстрый хеш-код с помощью .to_h, а затем использовать его следующим образом:

balance_category = ...where(...)...pluck(...).to_h

balance_category[:asset]

Если вам нужно значение по умолчанию, рассмотрите:

balance_category = Hash.new(0).merge(...where(...)...pluck(...).to_h)

Где это значение по умолчанию может быть целым числом (0) или с плавающей запятой (0.0) или чем-либо вообще.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...