LoginSignup
23

More than 5 years have passed since last update.

[Rails]スレッドセーフにskip callbackする方法

Posted at

はじめに

最近RackサーバーをPumaに切り替えたこともあり、スレッドセーフのチェックをがんばっている日々。特定の条件で動的にcallbackをスキップしている箇所があるが、set_callback / skip_callbackはスレッドセーフでないので使えない。じゃあどうしたら良いのよ!?というのがきっかけで、skip_callbackと同等な動作をスレッドセーフでやる方法を調べてみた。

素朴な疑問(:callbacks => falseオプションが実装されていない経緯)

ActiveRecordの作成・更新系のメソッドにはvalidationをスキップするオプションがある(:validate => false)。だったらそもそも、callbackをスキップするオプションだって実装されていても良いのでは?という素朴な疑問が浮かんだ。調べてみると、railsのPull Requestで同じことを考えている人がいた。
https://github.com/rails/rails/pull/4885

結論としては、callbackをスキップするオプションが欲しいと考えてる人は多いようだが、結局railsには実装されず。理由は、Modelの外の条件でcallbackの有効/無効が切り替わるのはユースケースとして間違っている、そもそもcallbackを使うべきではない、ということ。じゃあ、update_columnsみたいなメソッドはどうなんだとか、:validate => falseのオプションはどうなんだとかツッコミが入っているが、、

skip_callbackのスレッドセーフな代替案

attr_accessorを使う

attr_accessorを使って、callbackの有効/無効の状態を保持しておく。たぶんこれが一番きれいな方法。

# Model
attr_accessor :skip_my_callback
after_save{ my_callback unless skip_my_callback }
#controller
Model.create skip_my_callback: true

Observerを使う

モデルの内部的なロジックに関係しないトリガーメソッドは、callbackではなくobserverを使うのがもともとのマナー(らしい)。
configで指定できるので、RAILS_ENVで有効/無効を切り替えたいときはこれが最適。
config.active_record.observers = [:user_observer]

callbackのない別モデルを作る

callbackのない別モデルを作り、そちらのモデルを使ってモデルの更新などを行う。

# Model
class PureUser < ActiveRecord::Base
  self.table_name = 'users'
end
# Controller
PureUser.new( user_attributes )

update_columns

DBを直接更新しにいくことで高速化を図ることが目的のメソッドだが、callbackをスキップするためにも使える。ただ、validationもスキップしてしまうし、after_save/after_updateあたりの一部のコールバックだけにしか使えない。
同じようなメソッド:
update_column
update_columns
update_all
update_counters

gemを使う

サードパーティのgemを使う。ざっと調べて、以下のようなgemを見つけた。使うgemが本当にスレッドセーフか確認する必要があるが、、
https://github.com/einzige/sneaky-save
https://github.com/dball/skip_activerecord_callbacks

結論

RAILS_ENVでcallbackの有効/無効を切り替えたいときはObserverを使い、それ以外の場合はattr_accessorを使う。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23