ヨージとプログラミング

プログラミング勉強の記録

【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の本質を理解する必要はありそうです。