DelayedJobを使っていて、ちょっとジョブの状態を知りたくなったのですが、各ジョブがどんな状態かを明確に取得する方法がなく、少々ハマってしまいました。
ここにその時調べたりソース読んだりしたことをメモしておきます。
各状態のジョブを取得する
失敗ジョブを削除する場合
working = Delayed::Job.where('locked_at IS NOT NULL')
failed = Delayed::Job.where('last_error IS NOT NULL')
pending = Delayed::Job.where(attempts: 0, locked_at: nil)
enqueued = Delayed::Job.all
失敗ジョブを削除しない場合(これが大変)
working = Delayed::Job.where('locked_at IS NOT NULL').where(last_error: nil) # 再試行分は取れない…
failed = Delayed::Job.where('last_error IS NOT NULL')
pending = Delayed::Job.where(attempts: 0, locked_at: nil)
enqueued = Delayed::Job.all - failed
実行中ジョブ(working)
起動しているWorkerの数だけジョブが実行される。
実行されるとlocked_at, locked_byがセットされる。
失敗ジョブ(failed)
実行中ジョブが失敗すると、last_errrorにその失敗内容が、failed_atにその時刻が保存される。
この時attemptsが1増える。ここで、設定したattemptsの最大値に達していない場合はロックが解除され、再実行のためにリスケされる。最大値に達した場合は再実行されず、そのまま削除される。
ただし、Delayed::Worker.destroy_failed_jobs = false
としていると、失敗ジョブは削除されずに残る。
delayed_job/worker.rb at master · collectiveidea/delayed_job
失敗ジョブを削除しない場合
失敗ジョブを削除しない場合、残った失敗ジョブがロックされた状態のままになる。
そのためworkingのlocked_at IS NOT NULL
という判定に失敗ジョブが含まれるようになるので、取り除く必要がある。
しかし、単純にfailedを引けばよいかというとそうでもない。
locked_at, locked_byはDBには保存されないため、ワーカを再起動するとどちらもnilになる。
従ってDelayed::Job.where('locked_at IS NULL').where('last_error IS NOT NULL')
になっている失敗ジョブも存在する。
当然これも失敗ジョブとしてカウントされるので、ロックされているジョブだけを対象にしている実行中ジョブから引いただけでは数が合わない。
working = Delayed::Job.where('locked_at IS NOT NULL').where('last_error IS NULL')
ただ、これだと再試行の実行中ジョブは取ってくれない…。
下記のように、attemptsが最大に達しているかを見れば再試行中のジョブも取ってくれそう(未確認)
failed_in_running_worker = Delayed::Job.where('last_error IS NOT NULL').where('locked_at IS NOT NULL').where(attempts: Delayed::Worker.max_attempts)
working = Delayed::Job.where('locked_at IS NOT NULL') - failed_in_running_worker
attempts
attemptsはリスケのたびにインクリメントされる。
configでDelayed::Worker.max_attempts = 1
に設定したら、最大attemptsは1。
なので、この値が0のものはまだ実行されていないか、成功終了したものになる。
複数Workerでの実行
ruby - Delayed_job - Multiple parallel queues? - Stack Overflow
bundle exec ruby script/delayed_job -n 4 start
bundle exec ruby script/delayed_job stop
# rake taskを使う場合は、number指定ができない
# foremanのような複数プロセスの管理ツールを使うと良さそう
bundle exec rake jobs:work
bundle exec rake jobs:work
bundle exec rake jobs:work
# ちなみに、rakeは通常コマンドのqueue/queues相当の環境変数が使える
QUEUES=build_data,email rake jobs:work
その他注意
テストなどで、last_errorがあるジョブを作成するとき、MassAssignmentSecurity::Error
が発生する。
回避するには次のようにwithout_protection
オプションを指定する。
Delayed::Job.create({ last_error: 'error' }, without_protection: true)
Rails - seed.rbでCan't mass-assign protected attributesが出た場合の回避方法 - Qiita