Rails如何触摸Active Record Object而不锁定?

时间:2011-09-28 18:09:17

标签: ruby-on-rails caching locking associations touch

在我的照片课程中,我有这种关联。

belongs_to :user, :touch => true

有一天我得到了这个例外。

A ActiveRecord::StatementInvalid occurred in photos#update:

 Mysql::Error: Deadlock found when trying to get lock; try restarting transaction:
 UPDATE `users` SET `updated_at` = '2011-09-20 14:17:44' WHERE `users`.`id` = 6832
 production/ruby/1.8/gems/activerecord-3.0.10/lib/active_record/connection_adapters/abstract_adapter.rb:207:in `log'

我应该怎么做才能防止此类事件发生?我希望错误中显示的更新语句尽可能不使用锁定。我不认为在这种情况下使用乐观锁定会起作用,因为乐观锁定可能会引发ActiveRecord :: StaleObjectError。

1 个答案:

答案 0 :(得分:8)

这也是我偶然发现的一个问题。

简短回答:没有简单的解决方法。所有touch es都包含在同一个事务中,因此就是死锁。

长答案:我猜你需要触摸对象来使某些(从属)缓存无效。通常建议使用touch仅适用于有限数量的“关系”。例如。在更新评论时使文章无效。

我的解决方案是需要失效的数据库对象的异步收集(使用sidekiq作业)。我为它编写了自己的控制逻辑,用于定义在对象更改时需要使其他对象失效的对象。例如。评论==>制品

这样我们就可以通过更冗长的方式使依赖对象失效。另外,我使用Model.update_all无效,这比“触摸链”快得多。它解决了我们的死锁问题(并为我们的缓存失效添加了详细程度和性能)。

额外提示:请勿使用updated_at。如果DB对象真的因为另一个对象发生了变化而发生变化,这是值得商榷的。覆盖cache_key模型可让您轻松定义自定义缓存键,如"#{id}-#{valid_from}"valid_from可以是您在模型上定义的时间戳(并且您使用的时间戳代替updated_at)。