モジュール 07ターム3 · 信頼性とスケール8 ポスター項目

レジリエンスパターン

ログインすると、進捗を複数のデバイスで保存できます。ログインするまでは、このブラウザに保存されます。

なぜ重要なのか

外部呼び出し(データベース、決済ゲートウェイ、LLM プロバイダー、SMS)は、いつか必ず遅くなるか、失敗するか、途中で失敗します。AI が生成したコードは、ほぼ例外なく正常系だけを前提にしています。タイムアウトのない遅い依存先がひとつあるだけで、スレッド、コネクション、サーバーレスの同時実行数を使い果たし、アプリ全体を停止させかねません。

学習目標

  1. すべての外向き呼び出しにタイムアウトを設定し、その値をレイテンシ予算から決められる。
  2. 安全にリトライできる: idempotent な操作だけを、exponential backoff と jitter 付きで、上限を設けて。
  3. サーキットブレーカーとフォールバックで graceful degradation(段階的な機能縮退)を実現できる。
  4. backpressure と dead letter queue を使い、過負荷や poison message が連鎖しないようにできる。
  5. 意味のあるヘルスチェックを公開できる。

レッスン

7.1 タイムアウト(11)

  • 多くの HTTP クライアントのデフォルトのタイムアウトは無限か、数分単位です。接続タイムアウトとリクエストタイムアウトを明示的に設定してください。
  • 予算から逆算します: ページが 2 秒以内に応答しなければならず、3 つのサービスを順に呼び出すなら、それぞれには 2 秒を大きく下回る時間しか割り当てられません。
  • デッドラインを伝播させます: 呼び出し元の残り時間が 300 ms なら、2 秒かかる呼び出しを始めてはいけません。
  • LLM の呼び出し: ストリーミングを使い、最初のトークンまでのタイムアウトと全体のタイムアウトを設定します。

7.2 リトライと exponential backoff(12, 13)

  • リトライするのは 一時的な エラー(タイムアウト、429、502、503、504、コネクションリセット)だけです。400/401/403/422 は決してリトライしません。
  • リトライするのは idempotent な操作、または idempotency key を持つ操作だけです(04 · API とコントラクト)。
  • バックオフ: delay = min(cap, base * 2^attempt) に ランダムな jitter を加え、何千ものクライアントが一斉にリトライしないようにします(thundering herd)。
  • 試行回数(多くの場合 3 回)と合計時間に上限を設けます。Retry-After に従います。
  • リトライは 1 つの層 だけで行います。すべての層でリトライすると掛け算になります: ユーザーの 1 操作あたり 3 x 3 x 3 = 27 回の呼び出し。

7.3 サーキットブレーカー(10)

状態: closed(通常)→ 失敗が閾値を超えると open(即座に失敗させ、呼び出さない)→ クールダウン後に half-open(いくつかだけ通して様子を見る)→ 正常なら closed に戻る。 フォールバック と組み合わせます: キャッシュしたデータ、縮退した機能、キューに入れて「後ほどお知らせします」と伝える、またはセカンダリプロバイダー(例: 2 社目の LLM ベンダー)。

7.4 バルクヘッドと backpressure(44)

  • バルクヘッド: 依存先ごとにプールや上限を分け、遅い LLM がログインエンドポイントに必要なキャパシティを食いつぶさないようにします。
  • backpressure: コンシューマーが追いつけないときは、メモリが尽きるまでバッファし続けるのではなく押し返します(上限付きキュー、Retry-After 付きの 429/503、優先度の低い処理の load shedding)。

7.5 dead letter queue(20)

N 回処理に失敗したメッセージは、永遠にリトライするのではなく DLQ に移します(「poison message」はキューを詰まらせることがあります)。DLQ の滞留数にアラートを設定します。中身を確認し、修正し、再実行(replay)できるツールを作ります。

7.6 ヘルスチェック(56, 57)

  • Liveness: 「プロセスは生きているか?」軽量にし、依存先は決してチェックしません(そうしないと DB の一瞬の不調で全 Pod が再起動します)。
  • Readiness: 「今トラフィックを処理できるか?」重要な依存先をチェックし、false のときはインスタンスをロードバランサーから外します。
  • Startup: 起動の遅いサービス向けです。
  • 実際のユーザージャーニーを模した合成監視の結果を反映する、公開ステータスページ。

7.7 graceful degradation

機能ごとに決めておきます: この依存先が落ちているとき、ユーザーには何が見えるのか。それを文書化してください。例: レコメンドは非表示にする、決済は明確なメッセージを出してキューに入れる、AI アシスタントは永遠にスピナーを回すのではなく「一時的に利用できません」と表示する。

Lab 7: 手元のノート PC でカオスを起こす

アプリと依存先の間に Toxiproxy(または障害注入用のミドルウェア)を入れます。

  1. LLM プロバイダーに 5 秒のレイテンシを加えて観察します。タイムアウト、jitter 付きリトライ、サーキットブレーカー、フォールバックを追加し、もう一度観察します。
  2. 決済プロバイダーが 50% の呼び出しで 503 を返すようにします。二重請求が起きないことを証明します。
  3. キューに poison message を注入し、DLQ に入ってアラートが出ることを証明します。
  4. データベースを 30 秒間停止させます。Readiness は失敗し、Liveness は維持され、サービスは再起動なしで復旧します。

完了の定義

AI プロンプトパック

  • 「このリポジトリ内の外向きネットワーク呼び出しをすべて洗い出してください。タイムアウトがないもの、idempotent でない操作をリトライしているもの、フォールバックがないものを報告してください。」
  • 「タイムアウト、一時的なエラーに限った exponential backoff と full jitter によるリトライ、ホストごとのサーキットブレーカーを備えた共通 HTTP クライアントを作成してください。」