アプリケーション開発をしているとアドホックに実行したいスクリプトがある時や大量のデータを処理したいときなど、バッチ処理を書きたくなる時ってありますよね。
Railsでバッチ処理を書く際によく使われている方法として有名なものが下記の3つだと思います。
- rails runner
- スクリプトとしてバッチを書く
- rake task
- ビルドタスクとしてバッチを書く
- sidekiq
- 非同期処理としてバッチを書く
アプリケーションを新規開発する際、バッチ処理のトリガーをrails runnerを使用するかrake taskを使用するかで迷ったのでまとめてみました。
sidekiq使う場合もバッチ処理内で呼ぶ想定なのでsidekiqでのバッチ処理についてはまとめていません。
先に結論言っておくと『バッチ処理を行うという観点ではrails runnerもrake taskもそこまで変わらない』です。
Rails runner とは
- Railsのコマンドラインツールの1つ
- RubyのスクリプトをRailsの環境で動かせるもの
- Railsの環境を読み込んだ上で任意のRubyスクリプトが実行することができる
- -e stagingのように-eオプションでRailsの環境を指定することができる
- Rubyのコードを直接実行する方法とスクリプトファイルを指定する方法がある
こんな感じで書くことができます。
class BatchExample
def initialize
end
def exec
'何かバッチ処理'
end
end
% rails runner BatchExample.new.exec
事前事後に行う共通処理を別途定義してまとめたりすると便利です。
class Batch
def initialize
@slack = SlackNotifier.new
exec
@slack.send('バッチが終了しました')
end
end
class BatchExample << Batch
def exec
'何かバッチ処理'
end
end
% rails runner BatchExample.new
個人的にメリットデメリットは下記のようになるかなと思います。
- メリット
- Rubyのスクリプトなので自由度が高い
- デメリット
- Railsの環境を毎回読み込むので若干遅い
- バッチの置き場所が決まってない
Rake taskとは
https://railsguides.jp/command_line.html#bin-rails
- RakeはRubyで記述されたビルドツールでunixでいうMakeと一緒
- rake taskはビルドタスク
- 独自のDSLで書くことができる
- environmentを渡すことでRailsの機能も使える
- rake 'タスク名' もしくは rails 'タスク名'で実行することができる
- taskはlib/tasks以下に置く
namespace 'import' do
task :csv, ['target_file'] => :environment do |task, args|
'csvから読み込む処理'
end
task :yml, ['target_file'] => :environment do |task, args|
'ymlから読み込む処理'
end
end
# csvから読み込みたい時
% rails 'import:csv['target_file']'
# yamlから読み込みたい時
% rails 'import:yml['target_file']'
- メリット
- namespaceメソッドを使ってグループ化ができる
- taskの置く場所がきまっていて迷わない
- デメリット
- 処理の行数が長くなるとつらい
- 引数の渡し方に癖がある
- 一時的に使用するバッチの置き場所に困る
一長一短ありますが、Railsでバッチを実行するという役割においてはあんまり変わらないですね。
rake taskは、バッチの置き場所など考えることが減り統一感がでていいかもしれません。
rails runnerは、スクリプトに共通処理などを挟めたりできるので自由度が高くてよさそうです。
一時的に実行するスクリプトはrails runner、定期実行するものはrake taskという分け方もあるそうです。
個人的には一時的に実行するスクリプトも定期実行する可能性がでてくることがあるので、最初からrails runnerに寄せるのが好みです。
番外編
バッチの置く場所について
rails runnerを使用する場合、バッチをどこに置くか問題があります。
その時のディレクトリ構成について弊社で使用しているディレクトリ構成を紹介します。
バッチは大きく分けて下記3種類にわけられます。
- 一度きりしか使わないもの
- 運用でたまにしようするもの
- 定期実行されるもの
これらが混ざっているとバッチが増えた際に、どのバッチが不要でどのバッチが使われているかが煩雑にわかりづらくなってしまいます。
そのためそれぞれの用途でディレクトリを切って下記のようなディレクトリ構成にしています。
batch/
├ temp/(一度きりしか使わないもの)
├ operation/(運用でたまにしようするもの)
└ cron/(定期実行するもの)
運用でたまにしようするものと定期実行するものは分けても分けなくてもいいですが、一時的に使用するものをtempフォルダなどに分けることでこのディレクトリ以下のバッチはいつ消してもよいという共通認識が取れるためバッチの棚卸しをしている時に役立ちます。
みなさんもよきバッチライフを!