【问题标题】:Potential race condition in Rails 4Rails 4 中的潜在竞争条件
【发布时间】:2013-09-24 01:43:36
【问题描述】:

给定用户和交易模型:

class User < ActiveRecord::Base
  has_many     :transactions
end

class Transaction < ActiveRecord::Base
  belongs_to :user
end

问题出现在 API 调用过于频繁:

def trx
  User.transaction do

    user       = User.where(id: @data.user.id).lock(true).first
    trx_number = user.transactions.count + 1

    # some work

    user.transactions.create(...)

    change_balance = ...

    user.balance = user.balance.to_f + change_balance.to_f

    inform_user = user.has_valid_informee

    if !result && inform_user
      inform_user.balance     = inform_user.aff_balance.to_f + @amount.to_f
      inform_user.save!
    end

    user.save!
  end

end

首先,我有一个竞争条件,仅使用 @data.user 更新用户余额,有时新请求在之前完成,因此我们丢失了一些数据。相反,我开始请求锁定该用户。

下一个竞争条件是计算总交易量,正如我现在看到的那样,我们将拥有另一个带有 inform_user 的 RC。

所以问题是我做错了吗?用以下方式重写inform_user的更新余额是否是一种好方法:

    inform_user.with_lock do
      inform_user.balance     = inform_user.aff_balance.to_f + @amount.to_f
    end

还有一个问题,它是使用嵌套锁的好方法吗,例如当我需要更新关系模型同时更新父模型时?

【问题讨论】:

    标签: ruby-on-rails activerecord race-condition


    【解决方案1】:

    您需要根据数据库的内容(而不是您的 Rails 模型当前拥有的内容)通过添加/减去来直接更新数据库。看看update_counters

    http://api.rubyonrails.org/classes/ActiveRecord/CounterCache/ClassMethods.html#method-i-update_counters

    # For the Post with id of 5, decrement the comment_count by 1, and
    # increment the action_count by 1
    Post.update_counters 5, comment_count: -1, action_count: 1
    # Executes the following SQL:
    # UPDATE posts
    #    SET comment_count = COALESCE(comment_count, 0) - 1,
    #        action_count = COALESCE(action_count, 0) + 1
    #  WHERE id = 5
    

    【讨论】:

    • 感谢您的建议,是否可以使用 update_counters 而不是 Model.transaction 来更新用户余额,因为 Rails API 建议使用不适用于复杂模型的简单事务。当我在模型上创建或更新关联时,锁定模型的最佳方法是什么?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多