不确定是什么导致了死锁,但您可以在处理两条记录时添加悲观锁,这将阻止另一个请求处理它们,直到锁被释放,ActiveRecord
将等待锁释放后再继续。
User.transaction do
@u1, @u2 = User.lock.where(id: [1,2])
# Those two records are now locked, other transaction instances
# can't proceed till this transaction block is exited
Follow.create!(follower: @u1, followee: @u2)
end
# lock is released here
Note:通过id: [2,1]
不会按该顺序返回它们,因此您需要处理该情况。
Note 2:太多的锁定可能会影响您的整体应用程序性能,因为用户模型可能是一个频繁使用的模型,但我想这一切都取决于这些后续发生的频率。
Update: Here's a second way that also might work, first the follow model, no touches, but instead an
after_create
class Follow
belongs_to :follower
belongs_to :followee
after_create :touch_users
def touch_users
# no locking and direct database update
User.where(id: [follower.id, followee.id]).update_all(updated_at: :Time.now)
end
end
然后控制器会执行正常的事务,或者根本不执行,因为你不需要它
Follow.create!(follower: @u1, followee: @u2)
NOTE: #update_all
不会触发 activerecord 回调,并且查询直接在数据库上完成(如果有的话)after_update
方法,那么您可能希望避免使用此方法。