【Sidekiq】リトライ間隔をコントロールする
こんにちは!
ラブグラフエンジニアのひろです。
今回は、 Sidekiq の実行タイミングを制御する方法について書いていきます。
起きていたこと
すぐには直らないエラーが起きる Job が何度もリトライされ、エラー通知がたくさん届くことで、他の通知が埋もれてしまう問題がありました。
該当のエラーは別のチームに作業してもらう必要があり、即座に対応してもらえるような体制(優先度)になっていませんでした。
Sidekiq のリトライ仕様
Sidekiq は失敗した Job を 指数バックオフアルゴリズム を使って計算した時間ごとに、25回まで自動リトライをおこなってくれます。
(retry_count ** 4) + 15 + (rand(10) * (retry_count + 1))
一時的なエラーの場合はとてもありがたい機能ですが、すぐに直らないエラーの場合、数分以内にいくつもの同じエラーが発生することになります。
リトライ感覚のコントロール
では、リトライ間隔を自分たちで決めるにはどうすればいいのでしょうか。
Sidekiq Wiki に記載がありました。
class JobWithCustomRetry < ApplicationJob
include Sidekiq::Job
sidekiq_options retry: 5
sidekiq_retry_in do |count, exception, jobhash|
case exception
when SpecialException
10 * (count + 1) # (i.e. 10, 20, 30, 40, 50)
when ExceptionToKillFor
:kill
when ExceptionToForgetAbout
:discard
end
end
def perform(...)
end
end
sidekiq_retry_in
を Job 内に定義することで、希望するリトライ間隔を秒数で指定したり、即座に処理を終了させたりと、エラーの種類などによって柔軟な対応ができそうなことがわかりますね。
使用例
エラーメッセージで "Timeout" が返ってくる例で示します。
"Timeout" が含まれていた場合はすぐにリトライせず、2時間後にリトライが走るように設定しました。
class ExampleJob < ApplicationJob
sidekiq_options queue: :default
sidekiq_retry_in do |_count, exception, _jobhash|
# Timeout が返ってきた場合は、リトライを2時間後に設定
return 2.hours.to_i if exception.message.include?("Timeout")
nil # nil を返すとデフォルトのリトライ間隔になる
end
def perform
...
end
end
システムがタイムアウトする時には、すぐにリトライしても上手くいかないことが多いと思うので
sidekiq_retry_in に使って落ち着くのを待ってから再度試す。という挙動を実現できました。
終わり
sidekiq_retry_in を活用することで、 Sidekiq のリトライ間隔をエラーの種類やメッセージによって細かく制御できることがお伝えできたと思います。
他にも Sidekiq にはたくさんのオプションや設定があるので、みなさんもぜひ Sidekiq Wiki に目を通して活かせるものがないか探してみてください。
Discussion