Help us understand the problem. What is going on with this article?

DelayedJobでジョブの状態を取得する

More than 5 years have passed since last update.

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

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした