Rails で特定の Job だけリトライを無効化する

前提
- Rails 6.1.x 系
- Sidekiq 6.5.x 系

ActiveJob の機能を使う方法
discard_on
を使う
以下の例だと、 ActiveJob::DeserializationError
という例外が発生した場合それを握り潰して Job のリトライは行わない、という挙動になる。
class SearchIndexingJob < ActiveJob::Base
discard_on ActiveJob::DeserializationError
def perform(record)
# Will raise ActiveJob::DeserializationError if the record can't be deserialized
end
end
retry_on
を使う
以下の例では、ActiveRecord::Deadlocked
が発生したときにJobリトライが行われ、Jobの試行回数は最大3回行うことになる。
class RemoteServiceJob < ActiveJob::Base
retry_on ActiveRecord::Deadlocked, attempts: 3
def perform(*args)
# Might raise ActiveRecord::Deadlocked when a local db deadlock is detected
end
end
この試行回数を 1 以下の数字にすることでリトライを防ぐことができる
retry_on ActiveRecord::Deadlocked, attempts: 0
ここで注意なのは「Jobの試行回数」であり「リトライ回数」ではないこと

ActiveJob の罠
Job 実行のバックエンドに Sidekiq を利用している場合、 Sidekiq のリトライ機構が Active Job のそれとは別に機能していることに注意が必要である。
たとえば、以下のように rescue_from
で例外を捕捉して共通処理を行って例外を raise し直すようなロジックを書いていると、そこから raise された例外は Sidekiq によって捕捉され、そのリトライ機構によってリトライされてしまう。
class RemoteServiceJob < ActiveJob::Base
retry_on ActiveRecord::Deadlocked, attempts: 0
rescue_from StandardError do |e|
# なにか処理
raise e # ココで送出された例外は Sidekiq によって捕捉されてしまう
end
def perform(*args)
# Might raise ActiveRecord::Deadlocked when a local db deadlock is detected
end
end

Sidekiq の機能を使う
前述のように Sidekiq で例外が捕捉された場合にそれを無視したい。そのようなときは Sidekiq options というものが Job 単位で設定することができる。
以下の Job では StandardError
を捕捉して「なにか処理」を行った後例外を送出し直しているが、retry: false
であるため Sidekiq によるリトライは行われない。
class RemoteServiceJob < ActiveJob::Base
sidekiq_options retry: false
rescue_from StandardError do |e|
# なにか処理
raise e # ココで送出された例外は Sidekiq によって捕捉されてしまう
end
def perform(*args)
# Might raise ActiveRecord::Deadlocked when a local db deadlock is detected
end
end

Jobで例外が出たらリトライせず即時 Dead Job Queue に入れる
上の例だと、Jobが失敗したという事実が Sidekiq の Web UI から事後的に確認することができず、場合によっては問題があるかもしれない。
失敗した Job を Dead Job Queue に入れる場合は retry: 0
のように設定する。
- sidekiq_options retry: false
+ sidekiq_options retry: 0
そうすることで、Sidekiq WebUI の Dead Job Queue で失敗した Job を事後に確認することができる。