首页 > 解决方案 > Rails 事务不是原子的?

问题描述

我有一个我编写的会计系统,它遵循标准的复式记账法。

复式记账有一个称为“试算表”的功能,您可以在其中验证整个系统是否正确,因为当您运行它时,它总是等于0.00

我已经编写了测试,并且总是在系统“停止”时运行我的试算表,但是在为大量记录播种期间写入大量数据库负载的情况下,我注意到我的试算表在 10 次尝试中有 1 次是错误的。

当它处于静止状态(没有插入)时,它总是正确的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 这意味着它在插入中途时以某种方式选择......我不知道这是怎么回事?

我可以理解是否创建日记帐分录/行项目不在事务中并且它正在选择中途插入的行,但它应该只在事务结束后从整个组中选择?

标签: ruby-on-railsrubypostgresqlactiverecordruby-on-rails-5

解决方案


如果你想避免重复的陈述,把它折叠成一个,这种形式的东西:

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) 或任何东西。


推荐阅读