新手问题 请叫如何优雅的处理竞态条件?

tianzhen · 2016年01月30日 · 最后由 tianzhen 回复于 2016年01月30日 · 1496 次阅读

请各位看看我这个简单的范例代码:

rails g scaffold accounts total:integer
rake db:migrate
rails c
> acc = Account.new
> acc_id = Account.first.id
> acc1 = Account.find(acc_id)
> acc2 = Account.find(acc_id)
> acc1 == acc2 # true
> acc1.object_id == acc2.object_id # false

Account model class 有个 add_total 的方法

def add_total(number)
  new_total = total + number
  update_attribute(:total, new_total)
acc1.add_total(10)
acc2.add_total(10)

期望 Account.first.total 结果为 20 但却是 10

有什么问题?你期望是什么结果?

#1 楼@rei 抱歉抱歉,应该是这样的...

Account model class 有个 add_total 的方法

def add_total(number)
  new_total = total + number
  update_attribute(:total, new_total)
acc1.add_total(10)
acc2.add_total(10)

期望 Account.first.total 结果为 20 但却是 10

我查了下好像这是竞态条件,我给 Account table 加了 lock_version 的栏位,更新了 add_total 逻辑,但这样 acc1 把 table 给锁死了,无限停在 retry 中...请问要如何才能让 acc2 成功能够执行 update_attribute 并且最终得到 Account.first.reload.total 为 20 呢?

Account.transaction do
   begin
     update_attribute(:total, new_total)
   rescue ActiveRecord::StaleObjectError
     retry
   end
end

#3 楼 @rei 感谢 rei!再请教下,这样做有什么需要了解的风险吗?虽然 update_counters 能够满足需求,但感觉还是加乐观锁,让第二次写入失败更保险些,不知道我的理解是否正确

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册