【Rials】cronoのREADME通りに設定するとうまく行かないよという話
TL;DR
- cronoでrakeタスクをスケジュールするとき
Rake::Task['タスク名'].invoke
ではなくRake::Task['タスク名'].execute
で呼び出そう
cronoの公式通りにやると、最初の1回目しかrakeタスクが実行されない
cronoはrailsのgemで、ジョブスケジューラーです。ジョブには自身で定義したrakeタスクを渡すこともできます。rakeタスクはもちろんlib/tasks
配下に作成しますよね。
一方で、cronoのスケジュール設定ファイルはconfig/cronotab.rb
です。ここからrakeタスクを呼び出すのですが、公式の説明には次のようにありました。
# config/cronotab.rb require 'rake' Rails.app_class.load_tasks class Test def perform Rake::Task['crono:hello'].invoke end end Crono.perform(Test).every 5.seconds
(参考) GitHub - plashchynski/crono: A time-based background job scheduler daemon (just like Cron) for Rails
注目してほしいのはRake::Task['crono:hello'].invoke
のところ。
crono.rakeというファイルのhelloというタスクを呼び出しているのですが、そのときにinvoke
で呼び出しています。invokeの意味は後述しますが、愚直に公式のとおりにやったところ、rakeタスクは最初の一回のみ実行され、2回目以降は実行されませんでした(実際には実行されていたがnilになるようになっていた)。
考察:ジョブスケジュール中はタスクが実行中とみなされているため、invokeの2回目以降がnilになるのではないか
rakeタスクの呼び出し方にはinvokeの他にもexecuteがあります。この2つにどういう違いがあるかという、『invokeは実行中は1度しか同じタスクを実行しない』というのがあります。
(参考) rakeタスク内で別のタスクを呼び出す - Qiita
ほほう、とうことでrails consoleで試してみました。
# Rake::Task['weather:get']: 天気予報をDBに保存するrakeタスク # Rakeタスク1回目実行 [2] pry(main)> Rake::Task['weather:get'].invoke Weather Create ・・略・・ (7.2ms) COMMIT => [#<Proc:0x00005...] # Rakeタスク2回目実行 [3] pry(main)> Rake::Task['weather:get'].invoke => nil
たしかに1回目はきちんとDBにCommitできていますが、2回目はnilが返ってしまっています。ということは、やはりジョブスケジュールを実行している時はrakeタスクが完了しても完結してしまうわけでは無いみたいですね。(この表現があってるとは思ってない)
一方で、executeを使うと何回でもrakeタスクを実行できます。
# 1回目 [1] pry(main)> Rake::Task['weather:get'].execute Weather Create ・・略・・ (14.5ms) COMMIT => [#<Proc:0x00005...] # 2回目 [2] pry(main)> Rake::Task['weather:get'].execute Weather Create ・・略・・ (9.2ms) COMMIT => [#<Proc:0x00005...]
なので私はexecuteを使っています。
ただ、invokeを使う場合でも、invokeを使った後に毎回Rake::Task["task_name"].reenable
すると再度使えるようになるみたいです。
# 1回目 [1] pry(main)> Rake::Task['weather:get'].invoke Weather Create ・・略・・ (7.2ms) COMMIT => [#<Proc:0x00005...] # 2回目はnilになってしまう [2] pry(main)> Rake::Task['weather:get'].invoke => nil # 復活の呪文 [3] pry(main)> Rake::Task['weather:get'].reenable # 再生! [4] pry(main)> Rake::Task['weather:get'].invoke Weather Create ・・略・・ (19.1ms) COMMIT => [#<Proc:0x00005...]
ただ、executeで問題ないなら、ソッチのほうがシンプルなのでとりあえずexecuteでいってます。invokeやexecuteの本質を理解する必要はありそうです。