Ruby on Railsでバッチ処理を行なう場合、rakeタスクを使う方法と、rails runnerを使う方法の2パターンがあります。また、それぞれでActiveJobを使う場合とそうでない場合があるので、どのような場面でどの組み合わせを使うべきかをまとめてみました。
なお、この記事ではバッチ処理の定義を「大量のデータを処理するジョブ」とし、「定期的に自動実行されるジョブ」は含まないものとします。定期的に自動実行されるバッチ処理の場合はその旨を明示的に記載します。
Rakeタスク
Rakeタスクはcronのように自動的かつ定期的に実行されるバッチ処理、もしくは手動でも頻繁に実行するバッチ処理に向いています。
これは、rakeファイルの性質上、半ば強制的にネームスペースを含むタスク名と説明を記載することになり、自動的にドキュメンテーションの役目を果たすからです。
namespace :example do
desc "Create a CSV file for a given date."
task :create_csv, [:date] => :environment do |_, args|
end
end
Rakeタスク内でのActiveJob利用
通常のバッチ処理であれば、Rake内に全てのコードを書けば良いのですが、状況によっては以下のコードのようにRakeタスク内でActiveJobを使った方が良い場合があります。
namespace :example do
desc "Create a CSV file for a given date."
task :create_csv, [:date] => :environment do |_, args|
CreateCsvJob.perform_later(args.date)
end
end
1つ目は、サードパーティAPIに対して行ったネットワーク・リクエストが失敗した際にリトライしたい場合です。例えばリクエスト先のAPIサーバーが一時的にネットワーク障害で繋がらない場合、通常のRakeタスクではエラーとなってしまい、次のスケジュール実行時間まで待つ必要がありますが、Rakeタスク内でActiveJobに処理を渡すことで、システムが自動的に間隔を空けてリトライ処理を行ってくれます。
もう1つは、バッチ処理がメインのアプリケーションで既にActiveJobとして実装されている場合です。例えば、CSVファイルのダウンロードがアプリケーションUIで既に実装されていて、それをcronでも使いたいような場合です。このような場合、Rakeタスク内で同じようなコードを書くのではなく、既存のActiveJobを呼び出すことでコード重複を防ぐことができます。
逆に、自社サーバー内で全ての処理が完結するためネットワークエラーが起こる可能性がほとんどなく、例えエラーが起っても次のスケジューラー実行まで待て、さらにActiveJobで実装された既存のコードもない場合、Rakeタスク内のコードだけで完結させて良いでしょう。
なお、ActiveJob実行の際に条件分岐やループなどのコードロジックや引数が必要な場合はRakeファイル内にそれらのロジックを記載できるのでRakeタスクが適していますが、そういったロジックなしにただ定期的にActiveJobを実行したい場合はRakeタスクにする意味はあまりなく、後述するようにRails runnerでActiveJobを直接実行し、ドキュメンテーションはActiveJobファイル内に記載すれば十分でしょう。
Rails runner
一方Rails runnerは、以下のようなバッチ処理に使うのが向いています。
- 実行するのが一回きりの単発バッチ処理。
- 定期的にActiveJobをただ実行するバッチ処理。
実行するのが一回きりの単発のバッチ処理
一度きりの処理や、一時的な修正のためのスクリプトをRakeタスクにすると、Rakeタスク一覧(rake –tasks)が煩雑になる可能性があります。
このようなタスクはRailsコンソールで手動実行しても良いのですが、コードレビューや、各環境での実行の容易さ、コード履歴に残しておくことのメリットを考えると、スクリプトとして作成してバージョンコントロールした方が良いでしょう。保存場所は、アプリケーションルートにあるscriptディレクトリが一般的です。
ドキュメンテーションに関しては、スクリプト内に用途をコメントで記載しておけば十分です。
ActiveJobをただ実行するバッチ処理
Rakeタスクの項目で説明しましたが、ActiveJobを使う場合でも、ActiveJob実行に当たってコードロジックや引数が必要がない場合は、RakeタスクよりRails runnerが適しています。
そのような場合、以下のようにrails runnerで直接ActiveJobを実行するだけです。
rails runner ExampleActiveJob.perform_later
この方法の利点は以下の通りです。
- Rakeタスクのようにスクリプトを書く手間が省ける。
- ActiveJobの利点であるエラーの際のリトライが可能。
まとめ
RakeタスクとRails Runnerのどちらを使うべきか、及びそれぞれのケースでActiveJobを使うべきかを以下の表にまとめてみました。
| Rake | Rails Runner | |
| ActiveJobあり | ・定期的に実行する。 ・ネットワーク接続で失敗した時はリトライしたい場合。 ・メインのアプリケーションで使っている既存ActiveJobをバッチでも使いたい場合。 ・RakeスクリプトでActiveJobの実行条件をコントロールしたい場合。 | ・定期的に実行する。 ・ネットワーク接続で失敗した時はリトライしたい場合。 ・ActiveJobを使いたいが、実行条件をコントロールする必要がない場合。 |
| ActiveJobなし | ・定期的に実行する。 ・Rakeスクリプト内でロジックが完結する場合。 | ・実行するのが一回きりの単発バッチジョブ。 |
タスクの性質によりどの組み合わせが最適かは異なりますので、それぞれの利点を把握して適切なツールを使うようにしましょう。
