Rails 余额提取操作线程问题

Rails balance withdraw action threading issue

为了允许用户创建余额提取请求,我有一个 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

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

class 变量 Mutex 不是一个好的解决方案,因为这会为所有用户锁定此操作,而需要在每个用户级别上锁定。

最好的解决方案是什么?

下图说明了我怀疑的线程问题,它可能发生在 Rails 中吗?

据我所知,您的代码在这里是安全的,多线程并不是什么大问题。即使您的 应用服务器 生成了更多应用实例,每个实例最终都会测试 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