開発備忘録 2018/10/16
[Rails] バッチ処理のパフォーマンスについて考える
バッチで大量のデータに対して何かしらの処理を行うとしたときに、こんな感じで実装してみる。
User.all.each do |user| # 処理 end
ふむふむ、なるほど。
これが例えば User.all
で取得した件数が 100 件とかだったらまだ大丈夫。
しかしサービスが成長して会員数が 1 億人を突破しましたと。
そうするとこのバッチ処理は途端に破綻する。
何故かというと User.all
で取得した ActiveRecordRelation
は each
で回そうとしたときに 1 億件の ActiveRecord
をメモリ上に展開しようとします。
ActiveRecord
のインスタンスの生成はかなり重いので、1 億件ともなるとメモリが足りなくなり最悪バッチが止まってしまいます。
これを回避するためには、each
ではなく find_each
を使いましょう。
User.find_each do |user| # 処理 end
これにより (デフォルトでは) 100 件毎に user を取得し処理を行うようになります。
Rails で例外クラスを定義するときは Exception ではなく StandardError を継承するようにしよう
まず前提として、Exception
は全ての例外クラスの祖先にあたるクラスで、StandardError
はその子にあたる。
ここで Exception
を継承した MyError
クラスを定義し、適当に例外を補足するような実装をしてみる。
class MyError < Exception; end begin raise MyError, 'my error' rescue => error p error end
しかしこの場合は例外は補足されない。
Ruby では rescue の後にクラスを省略した場合、StandardError
の例外を補足するようになるが、MyError
は Exception
を継承しているため、この場合は例外が補足されない。
というわけで、今度は rescue で Exception
も補足できるようにしてみる。
begin raise MyError, 'my error' rescue Exception => error p error end
これで MyError
を補足できるになる。が、これは悪手だ。
Exception
を補足できるようにしてしまうと、OS レベルの例外も補足してしまうため、意図していない場面でアプリケーションが落ちてしまう可能性がある。
このような理由から Rails で例外クラスを定義する場合は StandardError
を継承する方が良い。