要点: 耐障害性は再試行回数を増やすことではなく、時間と資源に上限を持たせることから生まれます。
この記事の用語: Circuit Breaker:失敗中のサービス 呼び出しを一時停止する仕組み。Bulkhead:資源を分離して障害波及を防ぐ設計。
現場の課題
Timeoutがなければ資源を長く保持し、複数層の再試行は下流呼び出しを増幅します。共有接続プールでは、一社の遅延が全体の資源を枯渇させます。
単純な対策だけでは不十分な理由
遅い依存サービスは接続を使い切り、無制限再試行は弱っている下流へさらに負荷を加えます。呼び出しごとに期限、試行、資源プールを分けます。
処理フロー
残Budget→Bulkhead
分離同時実行数→試行
Timeout・冪等性→Circuit
Controlled 失敗
アーキテクチャ上の判断
業務フロー Budget
残時間をDownstreamへ配分します。
エラー Semanticsで再試行
Validationや非冪等 書き込みは再試行しません。
プール分離
遅いVendorを他依存サービスから隔離します。
さらに深く考える
FallbackはProduct判断
誤情報よりUnavailableが安全な場合があります。
Adaptive 上限 Guardrail
Min、Max、Slow 復旧を使います。
実装手順
- 処理全体の期限を伝播し、残り時間がなければ下流呼び出しを止めます。
- 冪等な操作の一時エラーだけを、少ない回数と待ち時間の段階的増加付きで再試行します。
- 依存サービスごとに接続と同時実行数を分離し、キューが無限に増える前に拒否します。
コード例: 期限-aware 呼び出し
remaining = request.deadline - now()
if remaining < minimumUsefulTime: return degraded()
with bulkhead.acquire(timeout = 20ms):
return retry(max = 2, jitter = true) {
vendor.call(timeout = min(300ms, remaining))
}試行ごとに残期限を再計算します。
想定しておく障害
- FallbackがStale過多です。
- Half-open Probeが多すぎます。
- クライアント離脱後も再試行します。
監視すべきこと
| 指標 | 何が分かるか |
|---|---|
| 依存サービス別期限消費 | Budget消費先です。 |
| 再試行 Amplification | リクエスト当たり試行です。 |
| Bulkhead Reject・Circuit | 封じ込め状態です。 |
設計の検証方法
- 失敗種別を個別注入します。
- 一依存サービス 失敗時の他プールを確認します。
- 部分復旧を試験します。
本番導入の進め方
現状の遅延と試行数を測り、まず明示Timeoutだけを追加します。次にプール分離、限定再試行、観測のみのCircuit Breakerという順で導入します。
本番前チェックリスト
- 再試行 担当者は一層です。
- CircuitにOverrideとProbeがあります。
- Fallbackのデータの新しさを明示します。
まとめ
壊れにくい外部連携は、障害を既知の上限内へ閉じ込め、健全な部分の能力を守ります。
