ActiveRecord::Batches のメソッドについて
はじめに
バッチ処理などで大量のレコードに対して処理を行わなければいけない時、全てのレコードを一度に読み込んでしまうとメモリ負荷の観点から非効率的である。
そのような時に使えるのが ActiveRecord::Batches のメソッド (in_batches
, find_in_batches
, find_each
) である。
これらのメソッドを使えば、大量レコードをいくつかの単位(デフォルトで1000件)に分割して処理を行うことができる。
in_batches
in_batches
メソッドでは、分割単位ごとに絞り込まれた ActiveRecord::Relation オブジェクトをブロックの引数として処理を行う。
使い方として、おおきく3つを挙げる。
①分割したレコードごとに一括処理
User.in_batches do |users| users.update_all atrributes end
②分割してレコードを読み込み、それを使い回すような処理
User.in_batches do |users| users = users.to_a OneService.call users AnotherService.call users end
③分割してレコードを読み込み、その1件ずつに対する処理
User.in_batches do |users| users.each do |user| user.do_something! end end
このうち②と③は、それぞれ find_in_batches
, find_each
で置き換えることができる。
find_in_batches
find_in_batches
メソッドでは、ブロックが渡された場合、処理の中で in_batches
が呼ばれるようになっている。
https://github.com/rails/rails/blob/v7.0.1/activerecord/lib/active_record/relation/batches.rb#L137-L139
in_batches(of: batch_size, start: start, finish: finish, load: true, error_on_ignore: error_on_ignore, order: order) do |batch| yield batch.to_a end
これを踏まえると②のコードは以下のように書き換えられる。
User.find_in_batches do |users| OneService.call users AnotherService.call users end
find_each
find_each
メソッドでは、ブロックが渡された場合、 処理の中で find_in_batches
が呼ばれるようになっている。
https://github.com/rails/rails/blob/v7.0.1/activerecord/lib/active_record/relation/batches.rb#L70-L72
find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do |records| records.each(&block) end
これを踏まえると③のコードは以下のように書き換えられる。
User.find_each do |user| user.do_something! end
まとめ
ActiveRecord::Batches に3つののメソッドがあり、それらは find_each
⊃ find_in_batches
⊃ in_batches
のような関係性にあることがわかった。
Railsアプリケーションで大量レコードの処理を行う際はこれらのメソッドの違いを理解し、処理内容によってどのメソッドが適切か考えた上で使うようにする。