TT Lab
はじめる
学ぶ 学習パス コース

キューと非同期API

指数バックオフとDLQを実装する

TT Labで続きを見る

目標

リトライの4つの要素(指数バックオフ、ジッター、上限、諦めたあとのDLQ)をすべて実装し、DLQに入った情報だけで原因を突き止めて、再処理まで完走します。

なぜ重要なのか

リトライは最も簡単に見えて、最も頻繁に事故を起こすコードです。固定間隔の無限リトライは、回復しようとするサーバーを、周期的にまた倒します。指数バックオフだけを入れると、1万のクライアントが依然として同じ瞬間にリトライして、波を作ります。ジッターまで入れても上限がなければ、10回目の試行で17分待ちます。そして、諦める条件がなければ、間違ったメッセージ1つがキュー全体を永遠に塞ぎます。これをポイズンメッセージと呼びます。DLQは、そのメッセージを隔離して残りを流すための仕組みであり、同時に調査対象の一覧です。そのため、メッセージだけを入れても役に立ちません。原因と試行回数と最初の時刻が一緒にあって初めて、原因を直して再処理できます。

ステップ

  1. /root/qr/worker.pyでq:tasksを消費するワーカーを作ります。/opt/app/taskproc.pyのprocess(msg)は、msg["kind"]がbadなら例外を投げます。
  2. 失敗したメッセージを、試行回数を1増やしてキューに戻します。/root/qr/requeue.outにattempts=2が見えている必要があります。
  3. /root/qr/schedule.txtに、base 1秒、試行0–5の指数バックオフの値をattempt=<n> wait=<초>の形式で6行書きます(プレースホルダーは秒数です)。1、2、4、8、16、32です。
  4. /root/qr/jitter.txtに、試行番号3のフルジッターの待ち時間を20個、1行ずつ書きます。すべて0以上8以下で、互いに異なる値が15個以上である必要があります。
  5. 上限30秒を適用して、/root/qr/capped.txtに試行0–8の値を書きます。最後の3つの値は、すべて30である必要があります。
  6. 5回失敗したメッセージは、q:tasks:dlqに送ります。q:tasksは空で、DLQの長さが1である必要があります。
  7. DLQメッセージのJSONに、payload、error、attempts、first_seen_atの4つのキーがすべてあり、attemptsは5です。
  8. /root/qr/replay.pyは、DLQから取り出してattemptsを0に戻し、q:tasksに入れます。実行後、DLQは空で、q:tasksに1件がある必要があります。

参考

キューを消費するワーカーを作る

/root/qr/worker.pyでq:tasksを消費するワーカーを作ってください。/opt/app/taskproc.pyのprocess(msg)は、msg["kind"]がbadなら例外を投げます。

メッセージを1つずつ取り出して、処理関数に渡します。処理関数は、わざと失敗できるように作っておいてください。

失敗したメッセージを再キューする

失敗したメッセージを、試行回数を1増やしてキューに戻してください。/root/qr/requeue.outにattempts=2が見えている必要があります。

試行回数をメッセージの中に入れておけば、次の消費のときに続けて数えられます。

指数バックオフのスケジュールを計算する

/root/qr/schedule.txtに、base 1秒、試行0–5の指数バックオフの値をattempt=<n> wait=<초>の形式で6行書いてください(プレースホルダーは秒数です)。1、2、4、8、16、32です。

試行番号を指数にします。計算だけを先にして、ファイルに残して確認してから、コードに入れてください。

フルジッターを適用する

/root/qr/jitter.txtに、試行番号3のフルジッターの待ち時間を20個、1行ずつ書いてください。すべて0以上8以下で、互いに異なる値が15個以上である必要があります。

0から計算された値までの間の乱数です。同じ試行番号でも、値が毎回変わる必要があります。

待ち時間に上限をかける

上限30秒を適用して、/root/qr/capped.txtに試行0–8の値を書いてください。最後の3つの値は、すべて30である必要があります。

指数はすぐ大きくなります。上限がなければ、10回目の試行で17分待つことになります。

5回失敗したらDLQに送る

5回失敗したメッセージは、q:tasks:dlqに送ってください。q:tasksは空で、DLQの長さが1である必要があります。

諦める条件があって初めてリトライです。元のキューからは消え、DLQにだけあるようにします。

DLQのメッセージに調査用の情報を入れる

DLQメッセージのJSONに、payload、error、attempts、first_seen_atの4つのキーがすべてあり、attemptsが5になるようにしてください。

元のメッセージ、失敗の原因、試行回数、最初に受信した時刻の4つがあって初めて、あとで調査ができます。

原因を取り除いてから再処理する

/root/qr/replay.pyは、DLQから取り出してattemptsを0に戻し、q:tasksに入れてください。実行後、DLQは空で、q:tasksに1件がある必要があります。

DLQから取り出して元のキューに戻し、試行回数を初期化します。自動のループではなく、明示的な実行である必要があります。