【Rails】バッチ処理を実装したいけどActiveRecordを中で使ったりもしたいし


Rails batch script

先日開発したRailsのAPIの仕上げ的な感じで、バッチ処理の実装をしました。

いままでフレームワークを使ったことがなかった時、何も考えずに適当なディレクトリにスクリプトを置いてcronに登録してヨロシク的なノリで作っていましたが、Railsの場合ってどうやってるのかー と思って色々調査をした時のメモ


目標

cronのような感じで定期的に自動実行する。
実行処理はDB内の特定の条件にひっかかるレコードの削除。
せっかくRailsで作っているのでActiveRecordとか中で使いたいよぅ。

方法

当たり前の話だが、MySQLのハンドラーとか自分で呼び出してヽ(`▽´)/!ワーイって開発し始めたら何の芸もない。
調べた所、
・runner コマンドでバッチ処理
・gem where によるスケジューリング
が丁度やりたいことを実現させてくれるらしい。

runnerコマンドで処理したいバッチの作成

定期的に走らせるプログラムを
RILS_ROOT/lib/tasks/ の直下に作成する。

今回は定期的にユーザーテーブルの中の条件にひっかかるレコードを削除する処理を走らせる事とする。

※論理削除しなさいよ等の批判お待ちしております。

vim lib/tasks/del_user.rb

require "#{Rails.root}/app/models/user"

class Tasks::DelTmpUsers
  def self.execute
    Rails.logger.debug("DELETE USER EXECUTE")
    User.delete_all("hogehoge")
  end

end

後述するrunnerコマンドではデフォではモデルのクラスとかrequireしないので個別で呼ぶのだそうですよ。

runnerで叩いて走るようにする

runnnerだけにね。

これは config/application.rb のロードパスを修正する必要があるんですって。
以下のように書くのがオシャンティらしい。

$ vi config/application.rb    # lib配下をロードするための設定 
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]

これでスクリプトの準備がケイオツだそうです。
$ rails runner Tasks::DelTmpUsers.execute
ターンっ と叩くと実行される。 logをtail -f で見張ってたら動いた。
(∩´∀`)∩ワーイ

cronで定期実行させるようにする

runnerコマンドの実装で前半戦終了なので、後半戦はスケジューリングをどうするのかな でした。

こちらは wheneverなるgemちゃんを使うと具合がよろしいようです。

Gemfileを開いて
gem ‘whenever’, :require => false
を記載して毎度おなじみの bundle install を実施。

次に、どの時間間隔(時刻)等で処理をさせるのか?的なものを記述するファイルを作成する。

そのために、ここで bundle exec wheneverize というコマンドを打つ。
※なんて詠むんだろう wheneverizeて。。

すると、 config/schedule.rb というファイルが自動で生成されている。
開けてみて最初に思った感想は「わかりやすぅーー」

もう crontab -e とか絶滅危惧種になるんじゃないかレベル。

見ての通りで、ここに時間の指定を書いてその中で呼出たいrunnner先輩を記述する。
たとえば5分毎にサンプラザ中野さんに歌ってもらいたいときは、、

every 5.minutes do
  runner "Tasks::DelTmpUsers.execute"
end

いや、ランナーだけに。
なお、runnnerの引数であるクラス・メソッドですが最初ダブルクォーテーションで囲わなかったら怒られたので囲ってみました。
なんで囲わないと怒られるのでしょうか?
知ってる方は教えてください。 

2014/8/18 追記
コメントで通りすがりの優しい方に教えてもらいました。
タブルクォーテーションで囲わないと、引数扱いされずメソッドとして実行され、その結果が入ってしまうからとのことでした。もっとrubyをちゃんと理解していないとアカンなぁと思いました。ありがとうございますm(__)m。

 

これをですね。サーバのcronに登録するんですね。

schedule.rbはあくまでcrontab に登録するお助けサポートだそうです。
いや、そういうと語弊がある気がしますが。 とりあえず運用もしやすいですしね。

実際の登録はこの呪文を読み上げるんだそう
bundle exec whenever –update-crontab

実際に声に出して読み上げると、結構かっこよくない響という事がわかると思います。

呪文の効果が現れているかcrontab -l で確認してみると、 勝手にcronをupdateしてくれてる。ありがとうございます。的な事が。

よく見ると productionモードなのかdevelopmentモードなのか といったような環境の指定もぬかりなく入っている。
素晴らしい。

まとめ

バッチ処理が1個や2個だけだったらさすがにwhenerveとか使わなくても全然いいと思いますが、どうせバッチ処理なんてサービスリリース後にじゃんじゃか増えるんだろうから今のうちにこういった機構を備えておくと心強いですね。
スクリプトも読みやすいし、わかりやすい。

おわり

   

2014-04-16 | Posted in Rails3 Comments » 


関連記事

コメント3件

 通りすがり | 2014.08.16 2:18

記事かいてから時間経ってますが…
> なお、runnnerの引数であるクラス・メソッドですが最初ダブルクォーテーションで囲わなかったら怒られたので囲ってみました。
> なんで囲わないと怒られるのでしょうか?
Rubyだとメソッド名をそのまま書くと()を省略してメソッドが実行されてしまいます。もし、クォーテーションで囲まないで書いたとするとexecuteの返り値が設定されてしまいます。
そのため、設定で書くときはクォーテーションで囲む感じです。

 altarfの管理人 | 2014.08.18 3:10

通りすがりの方ありがとうございます!

そうか!そうですね! 言われてみたて
ハッ!( ゚д゚)
っとしました。
もっとrubyの基礎を学ぶよう励んでいきたいと思います。
どうもでしたm(__)m

 Yusuke | 2014.11.21 12:17

I really wish there were more arciltes like this on the web.

Comment





Comment



*