Rails平衡撤销行动线程问题

时间:2016-01-21 09:00:32

标签: ruby-on-rails multithreading activerecord mutex banking

要允许用户创建余额提取请求,我有一个WithdrawalsController#create操作。在继续创建提款之前,代码会检查用户是否有足够的余额。

def create
  if amount > current_user.available_balance
    error! :bad_request, :metadata => {code: "002", description: "insufficient balance"}
    return
  end

  withdrawal = current_user.withdrawals.create(amount: amount, billing_info: current_user.billing_info)
  exposes withdrawal
end

这可能会在多线程服务器中造成严重问题。当两个创建请求同时到达,并且两个请求都在创建提款之前通过了余额检查,那么即使两个总和可能超过原始余额,也可以创建两个提款。

具有Mutex的类变量将不是一个好的解决方案,因为这将为所有用户锁定此操作,其中需要锁定每个用户级别。

最佳解决方案是什么?

下图说明了我怀疑的线程问题,是否可能在Rails中发生? Balance Withdrawal Threading Issue

1 个答案:

答案 0 :(得分:1)

据我所知,你的代码在这里是安全的,多线程并不是一个很大的问题。即使您的应用服务器生成了更多应用实例,每个实例也会最终测试amount > current_user.available_balance。 如果你真的很妄想它。你可以用transacaction包裹所有人:

ActiveRecord::Base.transaction do
  withdrawal = current_user.withdrawals.create!(
    amount: amount,
    billing_info: current_user.billing_info
  )

  # if after saving the `available_balance` goes under 0
  # the hole transaction will be rolled back and any
  # change happened to the database will be undone.
  raise ActiveRecord::Rollback if current_user.available_balance < 0
end