リトライパターンとは?指数バックオフ・ジッター・リトライ予算の実装判断を解説

リトライパターンは、失敗した処理のうち「もう一度送れば通る見込みのあるもの」だけを選んで再送し、待機時間と総試行数に上限を置く設計パターンです。難しいのは再送そのものではなく、どの失敗を対象に含めるか、何秒待つか、いつ諦めるかの3点にあります。この記事では、一時障害と恒久障害の切り分け、指数バックオフとジッターの計算式、試行回数・デッドライン・リトライ予算という3種類の上限、フレームワーク別の設定項目の対応、そしてリトライを設計から外すべき条件までを実装目線で追いました。数値はAWSとgRPCの公式ドキュメントを2026年8月5日に参照した内容に合わせています。

まとめ:リトライが成立する3条件と暴走を招く設計上の分岐点

先に結論を置きます。リトライが安全に成立する条件は3つ。対象の処理が冪等であること、失敗が一時的だと判定できること、そして総試行数と総待機時間に上限があること。1つでも欠けたまま再送を仕込むと、障害時に負荷を増幅させる側へ回ります。

待機の作り方は指数バックオフとジッターの組み合わせで固まっています。AWS SDKが2026年時点で採るのは乱数を掛けるフルジッター方式。基準遅延は一時障害50ms、スロットリング1,000msと種類で分けています。固定間隔を使う理由はありません。

崩れやすいのは上限の設計です。多層のサービスがそれぞれ3回ずつ再送すると、3層で最大27回。試行回数だけでなく全体デッドラインとリトライ予算を併せて置いてください。

リトライパターンの定義と、再試行で回復する障害を切り分ける判定基準

最初に決めるのは「何を再送するか」です。ここを広く取りすぎると、後段のバックオフ設計が無意味になります。

一時障害と恒久障害の分類、AWS SDKが再試行する3カテゴリの実例

AWS SDKs and Tools Reference Guideは、失敗を transient(一時)・throttling(スロットリング)・non-retryable(再試行不可)の3種類に分類しています。transientには RequestTimeoutInternalError、接続リセットやDNS解決失敗といったI/O障害、そして認識可能なエラーコードを持たないHTTP 500/502/503/504が入ります。

throttlingは ThrottlingExceptionSlowDown など、サービス側が明示的に流量を絞った応答です。対して AccessDeniedExceptionValidationException はnon-retryableとして即座に呼び出し元へ返します。権限不足や入力不正は、100回送っても結果が変わりません。

分類の順序も決まっています。同ガイドはエラーコードを先に照合し、次にHTTPステータスへフォールバックすると明記。スロットリングのエラーコードを持つHTTP 5XXは、5XXであってもthrottling扱いになります。

HTTPステータスとgRPCコードで引くリトライ可否の線引き

自前実装で判定表を持つなら、プロトコル層の対応を先に固定しておくと迷いません。gRPCの公式ガイドは retryableStatusCodes の例として UNAVAILABLE を挙げています。

応答 gRPCコード 再送 扱い
408 / 504 DEADLINE_EXCEEDED 条件付き 冪等な処理のみ。全体デッドライン内で1回
429 RESOURCE_EXHAUSTED する 基準遅延を長めに取り、Retry-Afterに従う
500 / 502 / 503 UNAVAILABLE / INTERNAL する 指数バックオフ+ジッター
400 / 422 INVALID_ARGUMENT しない 入力を直さない限り結果は同じ
401 / 403 UNAUTHENTICATED しない トークン再取得は再送ではなく別処理
409 ABORTED 条件付き 楽観ロック競合なら即時1回、業務エラーなら不可

判断に迷いやすいのが408と504です。タイムアウトは「サーバーに届かなかった」のか「届いて処理された応答が返らなかった」のか区別できません。後者の可能性がある以上、非冪等な処理でこの2つを再送すると二重実行になります。

冪等性が前提条件になる理由と、非冪等な処理を再試行に載せる手順

再送は「同じ要求を2回以上サーバーへ届ける」操作です。決済確定や在庫引当のように、2回実行されると結果が変わる処理では、成功しているのに応答だけ失われたケースで二重計上が起きます。前提となる考え方は冪等性とは何か、APIでどう担保するかの側に整理しています。

非冪等な処理を再送対象へ入れる場合、クライアント側に一意キーを持たせる手順を取ります。

  1. クライアントが要求ごとにUUID等の一意キーを生成し、Idempotency-Key ヘッダで送る
  2. サーバーはキーを主キーとして受付記録を挿入し、重複挿入を一意制約で弾く
  3. 初回はキーと応答本文を保存したうえで処理し、2回目以降は保存済み応答をそのまま返す
  4. 保存期間(24時間など)を決め、期限切れ後は新規要求として扱う

肝心なのは、キーを発行するのがクライアント側だという点です。再送のたびにキーが変わる実装では、サーバーは同一要求だと判定できません。

指数バックオフとジッター3方式の計算式、待機時間が伸びる速さの比較

待機時間の設計は、伸ばし方(バックオフ)と散らし方(ジッター)の2軸に分かれます。片方だけでは足りません。

固定間隔・線形・指数の待機時間比較と、上限(cap)を置く理由

基準100msで3方式を並べると、伸び方の差がはっきり出ます。

再送回数 固定間隔 線形 指数(倍率2)
1回目 100ms 100ms 100ms
3回目 100ms 300ms 400ms
5回目 100ms 500ms 1,600ms
8回目 100ms 800ms 12,800ms
10回目 100ms 1,000ms 51,200ms

固定間隔は障害が長引いたとき、負荷を一定のまま流し続けます。指数は回復待ちの効率が良い反面、上限がなければ10回目で51,200ms。AWS SDKは min(20,000ms, ...) で20秒の上限を置き、一時障害の基準50msなら10回目、スロットリングの基準1,000msなら6回目の再送で上限に当たると説明しています。

実務での既定は指数+上限です。線形を選ぶのは、下流の処理能力が既知で待機の予測可能性を優先する社内バッチに限られます。

ジッター3方式の待機分布の違いと、実装で採るべき算出式の選定

ジッターは待機時間に乱数を混ぜ、複数クライアントの再送タイミングが揃うのを防ぐ仕組みです。1,000台が同時に503を受けた場合、ジッターなしでは全台が同じ瞬間に再送し、回復しかけたサービスを再び倒します。

  • フルジッター:random(0, min(cap, base × 2^n))。0から上限までの一様分布。分散が最も大きい
  • イコールジッター:half + random(0, half)(halfは算出値の半分)。最低待機時間を保証しつつ散らす
  • デコリレーテッドジッター:直前の待機時間を種に次を決める。上限に達するまでの伸びが速い

採用するのはフルジッターです。AWSのリファレンスガイドも同方式を採り、再送をバックオフ窓全体へ一様に広げることで、同期したスパイクではなく細い流れをサービスへ届けると説明しています。イコールジッターは最低待機を確保できる代わりに集中が残ります。最低待機が要るなら、方式を変えるより基準遅延そのものを上げるほうが調整しやすい設計です。

AWS SDKのバックオフ式と、エラー種別で変わる基準遅延50ms・1,000ms

AWS SDKの算出式は delay = random(0,1) × min(20,000ms, base_delay × 2^retry) で、retry は初回再送を0として数えます。基準遅延は一時障害が50ms、スロットリングが1,000msで20倍の開き。DynamoDBとDynamoDB Streamsのクライアントだけは低遅延プロファイルに合わせ、一時障害の基準を25ms、最大試行回数を4に変えています。

この20倍差の理由は失敗の性質の違いです。一時障害はミリ秒単位で解消することが多く、短く刻んだほうが回復が速い。スロットリングは容量不足の通知なので、間を置かなければ結果は変わりません。自前実装でも、この2種類を同じ基準遅延で扱わないでください。

gRPCは別方式です。公式ガイドによれば initialBackoff(例:0.1秒)から backoffMultiplier(例:2)で伸ばし、maxBackoff(例:1秒)で頭打ちにしたうえで、算出値へ±20%のジッターを自動適用します。乱数の掛け方が違うため、両者を併用する構成では待機分布が揃いません。

試行回数・タイムアウト・リトライ予算という3種類の上限の決め方

上限は1種類では足りません。試行回数を絞っても、1試行が60秒待つ実装では全体で3分かかります。

最大試行回数の既定値と、多層リトライで総試行数が積算する計算

AWS SDKの max_attempts 既定値は3で、これは初回1回+再送2回を意味します。1に設定すれば再送を完全に無効化できます。gRPCの maxAttempts も同じく初回を含む総試行数です。

問題は多層構成です。API Gateway、バックエンドサービス、データストアクライアントがそれぞれ3回試行する構成では、最下層への到達回数は3×3×3で最大27回になります。層が1つ増えるごとに指数で膨らむ計算です。

対策は「再送する層を1つに決める」こと。障害を最も正確に判定できる層へ再送を集約し、他層は max_attempts=1 で素通しにします。中間層すべてに既定値のまま再送を残す構成が、障害時に負荷を増幅させる典型です。

全体デッドラインと1試行あたりタイムアウトを二層で置く設計手順

タイムアウトは2階層で持ちます。1試行あたりのタイムアウト(per-try)と、再送を含む全体のデッドラインです。gRPCはデッドラインを呼び出し全体に対して設定し、Istioのようなサービスメッシュも perTryTimeout と全体のタイムアウトを分けて指定できます。

設定順序は全体デッドラインから決めます。ユーザーが待てる時間が3秒なら、per-tryを1秒、最大試行数を3にしても、バックオフの待機を足すと3秒を超えかねません。per-try × 試行数 + バックオフ合計 ≤ 全体デッドライン が成り立つかを電卓で確かめてから値を入れてください。

成り立たない場合に削るのは試行回数です。per-tryを短くすると、正常時に間に合っていた処理まで切り、失敗率を上げます。

トークンバケット型リトライ予算が働き出す失敗率22%・32%の目安

3つ目の上限がリトライ予算です。AWS SDKのstandardモードは再送用のトークンバケットを持ち、容量500トークン、一時障害の再送で14トークン、スロットリングの再送で5トークンを消費します。再送が成功すればその分を戻し、初回で成功した要求は1トークンを戻す設計です。残高が尽きると再送を止め、エラーを即座に返します。

同ガイドは、既定の3試行では「一時障害が継続的に約22%を超えたとき、スロットリングなら約32%を超えたとき」に予算が減り始めると数値を示しています。裏を返せば、失敗率が数%程度に収まる通常運転では予算機構は何も起きません。

gRPCが持つ同種の仕組みが retryThrottling です。失敗したRPCがトークンを1減らし、成功が tokenRatio 分だけ増やす方式で、残高が maxTokens の半分を下回るとリトライを一時停止します。予算の考え方はサービス全体の流量制御と接するため、429の設計まで含めるならレートリミットのアルゴリズムと429応答の設計を併読してください。

SDK・フレームワーク別のリトライ設定項目と2026年のAWS既定変更

設定名は実装ごとに違いますが、押さえる概念は共通です。移植時は名前ではなく概念で対応を取ります。

AWS SDK・gRPC・Resilience4j・Pollyの設定項目の対応関係

概念 AWS SDK gRPC service config Resilience4j(2系) Polly(8系)
総試行数 max_attempts maxAttempts maxAttempts MaxRetryAttempts(初回を除く)
基準待機 base_delay(種別で固定) initialBackoff waitDuration Delay
伸び方 2^retry 固定 backoffMultiplier 指数の倍率を明示指定 BackoffType
上限 20,000ms 固定 maxBackoff 最大待機時間を指定 MaxDelay
ジッター フルジッター固定 ±20% 自動 randomizedWaitFactor UseJitter
対象の判定 エラーコード分類 retryableStatusCodes retryExceptions ShouldHandle
予算・遮断 retry quota(500トークン) retryThrottling CircuitBreaker と併用 ResiliencePipeline で合成

Resilience4jは2026年3月15日に2.4.0がリリースされており、2系が現行の系列です(2026年8月5日時点)。次期メジャーの3系はJava 21を要件とする方針が示されています。Pollyは8系で ResiliencePipeline に統合され、従来の IAsyncPolicy 等のインターフェイス群が一本化されました。再送・遮断・タイムアウトを1つのパイプラインへ並べる構成のため、7系からの移植では順序の指定を見直します。

AWS_NEW_RETRIES_2026のopt-inと、移行前に確認する3項目

AWSのリファレンスガイドは2026年8月5日時点で、この記事に挙げた挙動が既定になるまでオプトインが要ると明記しています。環境変数 AWS_NEW_RETRIES_2026=true を設定しない場合、SDKは2026年以前の挙動を使い、バックオフのタイミング・リトライクォータのコスト・サービス固有の既定値が異なります。

切り替え前に見るのは3点です。第一に全体デッドライン。基準遅延やクォータのコストが変わるため、最悪ケースの所要時間を再計算します。第二にレガシーモードの利用箇所。legacyはJava・Python・Ruby・PHP・C++・CLIのみで、.NET・Go・Rust・JavaScriptには存在しません。第三にSQSの ReceiveMessage などロングポーリング系で、クォータ枯渇時にも強制的なバックオフが挟まります。

adaptiveモードは一般的な既定として推奨されていません。単一リソースへ高頻度に呼ぶバッチ処理などに限った選択肢で、クライアントインスタンス単位でレート制限が働くため、複数リソースを扱うクライアントでは無関係なリソースへの要求まで遅延します。

リトライを設計から外す条件と、リトライストームを招く失敗パターン

ここは判断を言い切ります。再送は万能の保険ではなく、条件を満たさない場所では害のほうが大きくなります。

リトライを設計に入れるべきでない3条件と、代わりに置く仕組み

次の3条件のいずれかに当たるなら、再送を入れません。第一に、処理が非冪等でキー方式の重複排除も入れられない場合。外部の決済代行がキーを受け付けない仕様なら、再送ではなく人による確認フローへ倒します。二重計上の損害は、失敗した1件を救う利得より大きいためです。

第二に、呼び出し全体のデッドラインが再送1回分の待機より短い場合。ユーザー操作に同期する画面遷移で、500msしか猶予がない処理に再送を入れても、待機中に全体タイムアウトへ落ちます。ここは即時失敗を返し、画面側で再操作を促す設計にします。

第三に、失敗の主因が下流の容量不足である場合。容量が足りていないところへ再送を足すのは、渋滞している車線に車を増やす行為です。この場合はキューを挟んだ非同期処理への切り替えか、流量制御の導入が先に来ます。

サーキットブレーカー・レートリミットとの責務分担と併用の境界

3つの仕組みは目的が違います。リトライは「単発の失敗を吸収する」、サーキットブレーカーは「継続的な失敗を検知して呼び出し自体を止める」、レートリミットは「そもそも流す量を絞る」。責務の境界は時間軸で分かれます。リトライが見るのは1リクエストの範囲、ブレーカーが見るのは直近の失敗率、レートリミットが見るのは単位時間あたりの件数です。

併用の順序は、内側からリトライ、その外側にサーキットブレーカーです。逆順で組むと、ブレーカーが開いた状態からの応答をリトライが再送し続け、遮断の効果が薄れます。さらに外側へ、依存先ごとに並列枠を区切るバルクヘッドパターンを置くと、再送が消費する枠を依存先の単位で封じ込められます。サーキットブレーカーの状態遷移と実装を先に押さえたうえで、パイプラインの並び順を決めてください。メッセージング経路では、Outboxパターンによる確実なイベント発行と組み合わせると、at-least-once配信の重複を受信側の冪等化で吸収する形に整理できます。

この3層をどこまで自前で持ち、どこからマネージドの機能に寄せるかは、連携先の仕様と障害時の業務影響で変わります。外部連携を組み直す段階で判断材料が要るなら、API開発・システム連携の相談窓口で実装方針を相談できます。

実装レビューで潰すリトライ設計の失敗パターン4件と検知の方法

コードレビューで潰したいのは次の4つです。

  • ジッターなしの指数バックオフ:待機時間の算出に乱数が入っていない。負荷試験で同時多発障害を起こすと、再送が波形として現れる
  • 多層の既定値放置:中間層のクライアントが max_attempts 既定のまま。層ごとの設定値を一覧にすると積算がすぐ見える
  • 400番台の一律再送:例外クラスだけで判定し、retryExceptions にHTTPクライアントの汎用例外を入れている。ステータスコード単位の判定へ落とす
  • ログに試行回数が出ない:何回目の試行かをログに含めていないと、障害時に増幅の有無を確認できない。相関IDと試行番号を必ず出力する

リトライストーム(再送が再送を呼び、正常な要求まで押し流す状態)は、これら4つのうち複数が重なったときに起きます。単発の設定ミスで即座に破綻しないぶん、平時のレビューでは見過ごされがちです。負荷試験に「下流が10秒間503を返す」シナリオを入れ、上流の要求数が何倍に膨らむかを実測してください。

よくある質問

リトライの実装で問い合わせの多い論点を5つ挙げます。

リトライは何回に設定すればよいですか?

AWS SDKの既定は初回を含めて3回(初回1回+再送2回)で、DynamoDBのクライアントだけ4回です。まずこの3回を出発点にしてください。増やすなら、全体デッドラインとの整合を確認したうえで判断します。減らす選択肢もあり、中間層では1(再送なし)に落として再送を1箇所へ集約するほうが全体の負荷は下がります。回数を決める前に、何層でリトライが動いているかを数えるのが先です。

指数バックオフとジッターは両方必要ですか?

両方入れてください。指数バックオフは1クライアントから見た間隔を伸ばしますが、複数クライアントが同じ瞬間に障害を検知すると、伸ばし方が同じである以上タイミングも揃います。AWSのリファレンスガイドは、1,000台が同時に503を受けた例を挙げ、フルジッターがその再送を50msの窓へ一様に分散させると説明しています。ジッターは乱数を1つ掛けるだけなので、実装コストに対する効果が大きい部分です。

タイムアウトエラーはリトライしてよいですか?

処理が冪等なら再送して構いません。非冪等なら再送しないのが原則です。タイムアウトは「届かなかった」のか「処理は完了したが応答が返らなかった」のかを区別できず、後者で再送すると二重実行になります。再送したいなら、Idempotency-Key による重複排除をサーバー側へ入れてから有効化してください。接続確立で失敗した接続タイムアウトは要求が到達していない可能性が高く、読み取りタイムアウトとは分けて扱えます。

リトライとサーキットブレーカーはどちらを先に入れるべきですか?

リトライが先です。単発の一時障害は再送で吸収でき、実装量も小さくて済みます。サーキットブレーカーを足すのは、下流の障害が数十秒から数分続いたときにスレッドやコネクションが枯渇する構成になってからで構いません。両方を並べるときは、リトライを内側、ブレーカーを外側に置きます。逆順にすると、遮断中の失敗応答を再送が繰り返し、遮断の意味が薄れます。

リトライパターンとリトライストームの違いは何ですか?

リトライパターンは再送を制御する設計手法、リトライストームはその制御が効かず再送が連鎖増幅した障害状態を指します。多層での積算、ジッター不在による同期、上限のない試行回数が主な原因です。防ぐ手立ては、再送する層の集約、フルジッターの適用、トークンバケット型のリトライ予算の3点。AWS SDKのstandardモードは容量500トークンの予算を持ち、一時障害の継続率が約22%を超えると再送を絞り始めます。

関連記事

資料請求

RELATED POSTS 関連記事