Rails中潜在的竞争条件4

时间:2013-09-23 16:54:31

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

给定用户和交易模型:

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

另一个问题是,这是一种使用嵌套锁的好方法,例如我需要在更新父模型时更新关系模型吗?

1 个答案:

答案 0 :(得分:2)

您需要根据数据库的内容(而不是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