1 再試行の概要
1.1 定義と目的
1.1.1 再実行する対象(処理・要求・ジョブ)
再試行は、いったん失敗した処理や、応答が得られなかった要求、実行に失敗したジョブを、条件を満たす場合に限り再度走らせる設計・運用の考え方である。対象は同期のAPI呼び出しに限らず、非同期イベントの処理、バックグラウンドでのバッチ実行、メッセージ配信など多岐にわたる。再試行の範囲は「同じ入力で同じ操作をやり直す」だけでなく、必要に応じて待機時間の追加や、失敗の種類に応じた分岐を含む。
1.1.2 成功率向上と可用性確保
再試行の主眼は、外部要因によって一時的に失敗したケースの成功確率を押し上げる点にある。通信遅延、短時間の混雑、瞬断、アップストリームの一時不調などは、時間の経過とともに回復することがあるため、ただちに失敗として扱うよりも再実行する方が全体の到達率を高めやすい。また、サービス全体の可用性を維持するため、利用者が遭遇する失敗回数を減らす目的で採用される。
1.2 再試行が必要となる状況
1.2.1 一時的な障害・リトライ可能な失敗
一時的な障害は、同一の処理が次の試行で成功しうるため再試行と相性が良い。例として、サーバ側の短時間のリソース不足、ワーカーの一時的な飽和、依存コンポーネントの瞬間的な停止、ロック獲得の競合が解除されるタイミングの変化などが挙げられる。加えて、エラーのうち「再試行可能」と分類できるものは、機械的に取り扱える余地がある。
1.2.2 通信・タイムアウト・混雑
通信系では、応答遅延や接続断がタイムアウトとして表面化することがある。ネットワークの揺らぎは一度の失敗だけで恒久的な原因が確定しない場合があるため、一定条件での再実行が効果を持つ。混雑についても、短時間の輻輳で失敗した要求が、待機後に処理可能となるケースがある。再試行が有効になるには、待機と再試行の設計が「回復する可能性」を見込める形になっていることが前提となる。
1.3 再試行のリスク
1.3.1 重複実行による副作用
再試行は成功率を上げる一方、同じ操作を複数回実施してしまう危険を伴う。たとえば課金処理、在庫引当、外部への通知、永続ストレージへの更新など、重複すると整合性や法的・経済的な影響が出る領域では副作用が顕在化しやすい。再試行の設計では、重複が起きても問題が残らないようにする仕組み、または重複を確実に抑止する仕組みが不可欠になる。
1.3.2 負荷増大と障害の連鎖
再試行は失敗を隠すように働くため、見かけ上のエラーが減ったとしても、裏側では試行回数が増え、負荷が上乗せされる。特にタイムアウトが短い設計で再試行間隔が不適切だと、短時間に多量の再送が集中し、依存先の回復をさらに遅らせることがある。結果として障害が波及し、連鎖的な劣化につながる。したがって「再試行は常に良い」ではなく、制御と観測により全体最適を作る必要がある。
2 再試行の設計原則
2.1 冪等性と副作用の管理
2.1.1 冪等性の考え方
冪等性は、同一の操作を複数回実行しても結果が変わらない性質を指す。再試行では、失敗と判断された後に実際には処理が完了していた可能性もあるため、二重実行が起きても状態が破壊されないことが重要になる。設計上は「入力に対する出力が安定しているか」「結果が一意に定まるか」を基準に、冪等な経路を優先して実装する。
2.1.1.1 受信側の重複吸収(重複排除キー等)
受信側で重複を吸収する方法として、重複排除キー(同一要求を識別するキー)を使い、同一キーの処理結果を再利用する設計がある。これにより、送信側が再送しても受信側は二度目以降を抑制できる。キーの粒度は、アプリケーションの意味論に合わせて定める必要がある。誤った粒度では別の要求を同一扱いしてしまうため、識別子の生成規則と保管期間の両面が設計対象となる。
2.1.2 トランザクションと整合性
状態更新を扱う場合、再試行と整合性の関係を明確にする必要がある。トランザクションによって途中状態を隠蔽したり、ロールバック可能な範囲を設けたりすることで、不完全な更新が外部へ漏れる事態を抑える。加えて、複数リソースにまたがる更新では、部分成功の扱いが問題になりやすい。ここでは「成功の確定点」をどこに置くか、再試行時に確定点より前に戻るのか、確定済みを再利用するのかを決めることが整合性維持の中心となる。
2.2 再試行条件の設計
2.2.1 再試行可能/不可能の分類
再試行条件は、失敗の性質に応じて分岐させるのが基本となる。再試行可能は、待機後に成功しうる性質のものに限定する。再試行不可能は、入力の不備や権限不足など、修正がない限り成功が見込めないものを含む。分類を誤ると、無限に近いリトライで負荷が増えたり、誤ってユーザ行為を無駄に繰り返させたりするため、エラー体系の理解と運用経験に基づく調整が必要となる。
2.2.2 エラー種別ごとの方針
タイムアウト、接続リセット、5xx系応答、混雑を示すステータスなどは、一般に再試行の対象になりやすい。ただし同じカテゴリでも、ヘッダや応答ボディの意味により扱いが変わることがある。たとえば同一URLでも認証不備は再送しても改善しないが、サーバ側の一時問題は待機後に改善する場合がある。方針としては「通信系の不確実性」と「アプリケーション意味での拒否」を分け、前者は再試行、後者は即時停止やエラー返却へ誘導するのが典型である。
2.3 回数制限と停止条件
2.3.1 最大試行回数
再試行には上限を設ける。最大試行回数は、成功確率の増分が頭打ちになる点と、負荷増大の許容範囲の折り合いで決める。試行を増やすほど遅延も伸び、最終的な失敗時にはユーザや他システムに影響が蓄積する。したがって「期待される改善が見込める回数」に限定するのが基本である。上限に達した場合は、失敗を上位へ返し、適切なフォールバックへ移行する設計が求められる。
2.3.2 失敗時のフォールバック
再試行の打ち止め後の振る舞いを決めておくことが重要である。即時にエラーを返すだけでなく、別の経路での処理、キューへの退避、ユーザへの再操作依頼、管理者通知、将来の再処理計画など、状況に応じた選択肢がある。フォールバックを設計しないまま上限だけ設定すると、どこへも進まず処理が不自然に止まる場合があるため、停止後の導線は事前に用意する。
2.4 待機時間戦略
2.4.1 一定間隔
一定間隔の再試行は実装が単純で、理解もしやすい。短い固定待機は回復が早い環境では効果が出るが、依存先が回復するまで待てない場合には無駄な試行が増える。逆に待機を長くしすぎると、失敗が確定するまでの時間が伸びる。固定値は環境差を吸収しにくいため、初期値を決めた後は観測により見直す運用が望ましい。
2.4.2 指数バックオフ
指数バックオフは、試行回数に応じて待機時間を増やす方式である。初回は短く、失敗が続くほど待機を長くするため、混雑や一時障害の回復に時間を与えやすい。負荷の急増を抑え、依存先に過剰な同時要求が押し寄せる状況を緩和する効果が期待できる。上限値(最大待機時間)を設け、待機が際限なく延びないよう調整するのが一般的である。
2.4.3 ジッター(揺らし)による同時再試行の回避
多数のクライアントが同時に再試行すると、再び同時に負荷が集中する「同期問題」が起きうる。そこで、待機時間にランダム要素を加えて散らすのがジッターである。これにより、同じタイミングで再送される確率を下げ、依存先への波形をなだらかにする。ジッターは指数バックオフと組み合わせられることが多く、設計の一貫性を保ちながら分布を調整することが重要になる。
3 実装パターン
3.1 アプリケーション側での再試行
3.1.1 ラッパー関数・共通コンポーネント
アプリケーション層では、再試行ロジックを呼び出し側に散らさず、ラッパー関数や共通モジュールとして集約するのが一般的である。これにより設定(上限、待機、対象エラー)を一箇所で管理でき、挙動のばらつきを減らせる。加えて、デバッグや監視で「同じ失敗がどのポリシーで処理されたか」を追いやすくなる。実装では、呼び出しごとに入力を変えつつも、ポリシーの統一を維持する設計が扱いやすい。
3.1.2 タイムアウトと組み合わせ
再試行はタイムアウトとセットで考える必要がある。通信が返ってこないまま待ち続けると、再試行の前にスレッドやワーカーを占有してしまうためである。一般に「各試行のタイムアウト」と「全体の時間予算(累積の上限)」を分けて制御する。前者が適切であれば、試行間の待機や次の試行開始が制御しやすい。後者があれば、ユーザ応答の遅延許容範囲を守りながら再試行を打ち切れる。
3.2 ミドルウェア/ライブラリによる再試行
3.2.1 HTTPクライアントの再試行設定
HTTP領域では、クライアントライブラリに再試行機能が組み込まれていることがある。設定では、再試行対象となる条件(ステータスコード、例外の種類、読み取り待ち/接続待ちの扱い)と、最大試行回数、待機時間戦略を指定する。特に安全性の観点では、送信メソッドやリクエストボディの扱いが重要になる。再試行が安全に成立する条件を前提に設定し、受信側の重複吸収が可能かどうかも整合させる。
3.2.2 メッセージングの再配信
メッセージングでは、配信失敗時に再配信することで処理を継続できる。ここでは、重複排除や順序制御、遅延の扱いが設計の中心になる。再配信は「同じメッセージが再び届く」事象を前提に、受信側の処理を冪等化するか、重複検知のキーを設計に含める必要がある。さらに、再配信の回数が上限に達した場合の扱い(デッドレターキューへの退避など)を決め、運用で追跡可能にする。
3.3 バッチ処理・ジョブの再試行
3.3.1 キューイングとリトライ
バッチやジョブの再試行は、キューに載せ直す形で実現されることが多い。失敗したジョブを一定時間後に再投入し、再実行のタイミングを制御する。キューの優先度や遅延機能を使えば、障害復旧を待つ間に全体の流量を整えられる。再投入の判断は、失敗の分類に基づいて行うのが基本で、修正不要な一時障害のみを対象にする方針が安定しやすい。
3.3.2 ステータス管理(成功・失敗・保留)
ジョブ管理では、成功・失敗に加えて「保留」などの中間状態を持つと運用が整理しやすい。再試行中はどの試行回数か、最後のエラーが何か、次の実行予定時刻はいつかを追跡できるようにする。これにより、障害時の調査や、上限到達後のフォールバック導線が明確になる。状態遷移のルールが曖昧だと、重複投入や取り消し漏れが起きるため、明文化と実装の整合が必要である。
3.4 分散環境での再試行
3.4.1 競合と整合性の考慮
分散環境では、同じ処理対象に対して複数のワーカーが同時に再試行する可能性がある。これにより競合が増え、ロック待ちや競争による失敗が増幅する場合がある。整合性を保つには、排他制御、楽観ロック、または処理対象単位の一貫した割り当てなどの考え方が必要になる。再試行は「失敗を繰り返す」だけでなく、「並行性の前提をどのように制御するか」まで含めて設計するのが望ましい。
3.4.2 サーキットブレーカとの併用
サーキットブレーカは、失敗が継続していると判断したときに、呼び出し自体を一時的に停止して回復を待つ仕組みである。再試行と組み合わせることで、無駄な再試行による負荷増大を抑え、依存先が回復するまでの時間を稼げる。運用設計では、ブレーカの判定条件(失敗率や連続失敗数)、復帰の挙動(試行を戻すタイミング)を決め、再試行の上限と整合させることが重要になる。
4 運用と観測
4.1 ロギングとトレース
4.1.1 何回再試行したかの記録
観測では、再試行の回数や実行パスを記録することが欠かせない。初回の失敗から何回目で成功したか、あるいは上限に達して打ち止めになったかを追跡できるようにする。ログには、対象の識別子や相関情報も添えると、後続の調査で試行の連鎖をたどりやすい。大量のログが負荷になる場合は、サンプリングや要約記録の方針を併用する。
4.1.2 要因分析のための相関情報
再試行の有効性と問題点を切り分けるには、エラー種類、遅延、応答コード、タイムアウト発生点などの相関情報が必要である。トレースでは、依存先ごとの待ち時間や例外の発生箇所を可視化できる。これにより、「失敗が一時的に集中したのか」「特定の経路だけ再試行が多いのか」「待機戦略が効いているか」といった観点を定量的に評価できる。運用では、再試行ポリシーと観測指標を結びつけて判断可能にすることが重要である。
4.2 メトリクスとアラート
4.2.1 再試行率・成功率
再試行率は、どれだけの要求やジョブが再実行されているかを示す指標である。再試行の増加は、依存先の不調やネットワーク事情の変化を示唆することがある。成功率は、再試行後に最終的に成功へ到達した割合を示す。再試行率が上がっているのに成功率が伸びない場合は、ポリシーの対象分類が不適切、もしくは待機戦略が合っていない可能性がある。
4.2.2 エラー率とタイムアウト率
エラー率とタイムアウト率は、再試行の前提条件を評価する基礎となる。特にタイムアウト率の急増は、通信品質の劣化、リソース枯渇、処理遅延のいずれかを反映することがある。アラート設計では、単純な「エラーが増えた」だけでなく、再試行による負荷増大を先回りで検知する観点が有用である。たとえば再試行数の急増と依存先の応答遅延が同時に起きた場合は、サーキットブレーカの強化などの措置を検討する材料になる。
4.3 性能への影響評価
4.3.1 レイテンシ増加の見積り
再試行は成功までの時間を延ばしうるため、レイテンシへの影響を評価する必要がある。分布(平均だけでなくパーセンタイル)で見ると、失敗時の挙動がユーザ体験に与える影響を把握しやすい。設計では、各試行のタイムアウトと待機時間の上限、そして最大試行回数から、最悪ケースに近い遅延の見積りを行うことが実務上有効である。これにより、再試行が提供品質を損なわない範囲を定められる。
4.3.2 待機時間が与える影響
待機時間は単に「待つ」だけでなく、システムの同時性とスループットに影響する。待機が長いと、再処理の完了が遅れ、キューが滞留することがある。一方で待機が短すぎると再試行の密度が高まり、依存先がさらに苦しくなる。指数バックオフやジッターはこのトレードオフを調整する手段であり、実測に基づく調整が望ましい。性能評価では、再試行による遅延だけでなく、リソース使用量の変化も合わせて見ると判断が安定する。
4.4 ルールの見直し
4.4.1 失敗パターンの学習
再試行は固定設定で運用すると、状況の変化に追随できなくなる。そこでログやメトリクスから失敗パターンを学習し、再試行可能/不可能の分類、対象エラー、待機戦略を見直す。たとえば特定の時刻帯に再試行が多い場合、混雑の周期性が示唆される。原因が解消されたら再試行率を下げることもできるため、学習は改善の循環として働く。
4.4.2 閾値調整と安全設計
再試行ポリシーの閾値(最大回数、待機上限、ブレーカ条件など)は、障害時の振る舞いに直結する。過度に緩いと負荷が膨れ、過度に厳しいと回復の機会を逃す。安全設計としては、全体時間予算、同時再試行の抑制、フォールバック導線の整備などを組み合わせ、想定外の増幅を防ぐ。運用では定期的な見直しに加え、重大インシデント後の事後検証を通じて調整を反映するのが一般的である。