【问题标题】:Rails transaction isn't acting atomic?Rails 事务不是原子的?
【发布时间】:2019-05-05 22:14:16
【问题描述】:

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

有一个称为“试算表”的双重记账功能,您可以在其中验证整个系统是否正确,因为当您运行它时,它始终等于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(NOT 在事务中):

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 这意味着它在插入中途时以某种方式进行选择............我不知道这是怎么发生的?

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

【问题讨论】:

  • 您需要执行SELECT account, SUM(...) WHERE account IN ('asset', 'liability', ...) GROUP BY account,因为这可能是一次性声明。 Rails 通过在 where(account: [ 'asset', ... ]) 类型范围内使用数组来支持这一点。
  • 我在一分钟前才真正意识到这一点????????‍♂️它是因为在单独进行选择时添加了有效交易。如果您将其标记为答案,那就错了,也许您可​​以输入实际的语法。谢谢!

标签: ruby-on-rails ruby postgresql activerecord ruby-on-rails-5


【解决方案1】:

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

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

【讨论】:

  • 我看到 pluck('custom SQL') 将被弃用的错误,你知道另一种方法吗?我原本打算做类似的事情
  • 是的,他们一直不鼓励这种做法,但我不确定在较新的 Rails 中最好的方法是什么。值得作为一个新问题提出或在这里找到一个例子。
  • 可能是pluck(Arel.sql('custom SQL')),与order('custom SQL') 的解决方法相同。
猜你喜欢
  • 2018-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-26
相关资源
最近更新 更多