ActiveJob の retry_on に jitter というオプションがあるの知ってますか?
僕は知らなかったです (・∀・)
Rails で ActiveJob の retry_on が同時に発火されるの何とかしたいなーと思って調べてたら、Rails 6 からは jitter というオプションが指定できるようになってて、デフォルトでリトライ間隔を 15% ランダマイズしてくれるとの事。Rate Limit の回避とかで便利。 https://t.co/kpIx6YVFEq
— サトウリョウスケ (@ryosuke_sato) February 27, 2021
jitter とは
Rails 6.1 から追加されたオプション で、 retry_on
の待ち時間に対して任意の割合でバラけさせてくれるようになります(デフォルト 15% )。
ActiveJob::Exceptions::ClassMethods - retry_on
:jitter
- A random delay of wait time used when calculating backoff. The default is 15% (0.15) which represents the upper bound of possible wait time (expressed as a percentage)
実装を確認してみた
このバラけさせ方が待ち時間に対して増えるのか減るのかが気になって実装を見てみました。増える方向でバラけるようです。
例えば wait: 60.seconds
で jitter: 0.5
の場合だと、最大 90
秒の待ち時間となります。
うっかり 100.0
とか指定すると最大 10000% 待ち時間が加算されちゃいそうです。
間違えて指定しないようにご注意下さい🙏
delay = seconds_or_duration_or_algorithm.to_i delay_jitter = determine_jitter_for_delay(delay, jitter) delay + delay_jitter
📝 jitter の計算処理は このあたり
どういう場面で使うの?
この機能が無かった従来だと、リトライ処理が一斉に起動してしまうという問題がありました。
例えば外部のサービスに API リクエストするような Job を作ったとします。API Rate Limit 超過の例外をハンドリングして、 retry_on
で N 分後にリトライするように実装します。
この時、 1000 件の Job が同時に実行され 900 件が Rate Limit 超過となった場合、N 分後に 900 件の Job が一斉にリトライされてしまい、再び 800 件がエラーとなってしまう。
これが何度も繰り返される、という現象が起こってました。
Rails 6.1 以前だと回避できないの?
従来の ActiveJob でも一応回避策はあって、 wait
に Proc
を与えてランダムな待ち時間を返すような処理を書くことで似たような動作は可能です。
retry_on SomeError, wait: -> { rand(60..90).seconds }