Rails как трогать Active Record Object без блокировки? - PullRequest
11 голосов
/ 28 сентября 2011

В моем классе фотографий у меня есть эта ассоциация.

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 Ответ

8 голосов
/ 18 июня 2013

Это проблема, с которой я тоже столкнулся.

Краткий ответ : Нет простого способа обойти эту проблему. Все touch находятся в одной транзакции, поэтому тупик.

Длинный ответ : Я полагаю, вам нужны сенсорные объекты для аннулирования некоторых (зависимых) кэшей. Обычно рекомендуемое использование touch работает только для ограниченного количества «отношений». Например. аннулирование статьи при обновлении комментария.

Моим решением была асинхронная коллекция (с использованием задания sidekiq) объектов БД, которые должны быть признаны недействительными. Я написал для него собственную логику управления, которая определяет, какие (другие) объекты должны быть признаны недействительными при изменении объекта. Например. комментарий ==> статья.

Таким образом, мы получили более подробный способ аннулирования зависимых объектов. Кроме того, я объявил недействительным использование Model.update_all, которое было намного быстрее, чем «цепочка касаний». Это решило наши проблемы взаимоблокировок (и добавило многословность и производительность к нашему аннулированию кэша).

Дополнительный совет: не используйте updated_at. Это очень спорно, если объект DB действительно изменился, потому что другой объект изменился. Перезапись модели cache_key позволяет легко определить пользовательский ключ кэша, например "#{id}-#{valid_from}". valid_from может быть меткой времени, которую вы определяете в своих моделях (и которую вы используете вместо updated_at).

...