resilience-failure について
リモート依存先や共有リソースのレジリエンスを設計するスキルです。可用性、遅延、再試行の安全性に基づく障害対策を選び、トレードオフや負荷時のシナリオを検討できます。
主な機能
- 01タイムアウトと再試行ポリシー
- 02サーキットブレーカーとバルクヘッド
- 03縮退運転とフェイルオーバー
- 04レート制限と負荷遮断
- 05負荷シナリオと監視
活用シーン
- 01依存先の遅延・停止時の動作設計
- 02過負荷や再試行ストームの防止
- 03サービス回復時の負荷集中の回避
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ proyecto26/ system-design-skills --skill resilience-failureインストール後、Claude Code で/resilience-failureを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/deep-dive.mdreferences/providers/aws.mdazure.mdgcp.mdgeneric.mdtemporal.md詳細
耐障害性と障害対応
一部が壊れたとき(実際に壊れます)に、障害がその範囲内にとどまり、エラーページや連鎖的な停止ではなく、機能が低下していてもユーザーが有用な回答を得られるようにシステムを設計します。ここを誤ると、依存先の遅延で済むか、全面的な障害に陥るかの違いになります。障害を最も増幅させるのは、多くの場合、システム自身の反応(リトライの集中やヘルスチェックの殺到)です。
これを検討するタイミング
リモート依存先、共有リソース、または SLA がある設計全般です。単一障害点を見つけ、依存先が遅いときや停止しているときに各呼び出しがどう動作するかを決め、サービスが過負荷になるのを防ぎ(レート制限)、復旧したサービスがバックログに押しつぶされずに再開する方法を計画する際に役立ちます。
検討しないケース
単一のプロセス内関数やベストエフォートのバッチジョブにサーキットブレーカーやバルクヘッドを適用しないでください。これらはプロセス間・ネットワーク間の呼び出し向けの仕組みです(YAGNI)。冪等性キーを用意せずに、冪等でない書き込みにリトライを追加しないでください(→ api-design)。副作用が重複します。可用性目標を満たす最も低コストな設計が最適です。必要のない「9」をさらに増やそうとすると、実際の複雑さが増します(「9」が実際に何をもたらすかは → back-of-the-envelope)。
まず明確にすること
- 可用性目標 — 何個の「9」を目指しますか。また、リクエスト単位ですか、それとも機能単位ですか?(→
back-of-the-envelope。) - 影響範囲 — この依存先が停止した場合、リクエスト全体を失敗させる必要がありますか。それとも機能を縮退させたり、非表示にしたりできますか?
- 冪等性 — その操作は安全にリトライできますか。できない場合、何があれば安全になりますか(キー、重複排除)?(→
api-design。) - レイテンシ予算 — 待ち続けるよりタイムアウトした方がよいと判断するまで、呼び出しはどれくらい待てますか?(→
back-of-the-envelope。) - 制限の対象とポリシー — ユーザー、IP、API キー、テナントのどの単位でレート制限しますか?ハード制限(拒否)ですか、ソフト制限(キューイング/調整)ですか?どれくらいのバーストを許容しますか?
選択肢
防御を重ねます。実際の設計では、複数の選択肢を組み合わせるのが一般的です。
- タイムアウト — すべてのリモート呼び出しに上限を設けます。あらゆる場面で使用してください。上限のない待機は、ほとんどの連鎖障害の根本原因です。
- バックオフとジッターを伴うリトライ — 一時的な障害に対し、間隔を延ばしながらランダムな遅延を加えて再試行します。一時的な不具合に対する冪等な呼び出しに使用してください。無条件のリトライは決して行わないでください。
- サーキットブレーカー — 障害が発生している依存先への呼び出しを停止し、即座に失敗させ、復旧を確認するためにプローブします。下流のサービスが停止または低速で、リトライが積み重なるおそれがある場合に使用します。
- バルクヘッド — 依存先ごとにリソース(スレッドプール、接続プール、キュー)を分離します。遅い依存先によって、他の依存先と共有するキャパシティが枯渇しないようにするために使用します。
- グレースフルデグラデーション — キャッシュ値や古い値、部分的な結果、デフォルト値にフォールバックするか、機能を非表示にします。エラーよりも、使えるものの品質が劣る回答の方が望ましい場合に使用します。
- レート制限/負荷遮断 — 受け付ける処理量に上限を設け、超過分を拒否または調整します。過負荷、悪用、または呼び出し元からのアクセス集中からサービスを保護するために使用します。
- 冗長化/フェイルオーバー — すべてのコンポーネントを N>1 台稼働させ、障害時にはスタンバイを昇格させます。単一障害点(SPOF)をなくすために使用します。(ヘルスチェック/LB フェイルオーバーについては
load-balancingを参照してください。)
レート制限アルゴリズム(トークンバケット、リーキーバケット、固定ウィンドウ/スライディングウィンドウ)とサーキットブレーカーの状態遷移については、references/deep-dive.md で詳しく説明しています。
トレードオフ
| 選択肢 | 解決できること | 悪化すること | 変更するタイミング |
|---|---|---|---|
| タイムアウト | ブロックされたスレッドを制限し、遅い呼び出しが呼び出し元をハングさせるのを防ぎます | 短すぎると誤った失敗が増え、長すぎると連鎖が起こります | 推測ではなく、依存先の p99 に合わせて調整します |
| リトライ+バックオフ+ジッター | 一時的な障害を乗り切ります | 負荷が増大し、冪等でない書き込みが重複します | ジッターを加え、試行回数に上限と予算を設け、冪等性を必須にします |
| サーキットブレーカー | すばやく失敗させ、障害のある依存先に回復の余地を与えます | 状態管理と調整が必要になり、一時的な障害で作動して過剰に負荷を遮断することがあります | フラッピングする場合は、しきい値や半開状態でのプローブ頻度を調整します |
| バルクヘッド | ある障害を専用のプール内に封じ込めます | ピーク時の利用率が下がり、サイズを調整するプールが増えます | ある依存先の過剰な負荷がほかを圧迫するとき |
| グレースフルデグラデーション | 依存先の停止中もユーザーへのサービスを維持します | 古い情報や部分的な結果を提供し、テストすべきコードパスが増えます | 正確性が必須の場合は、代わりにフェイルクローズにします |
| レート制限 | サービスを保護し、コストや不正利用を抑えます | 正当なバーストを拒否し、大規模環境では共有状態が必要です | 制限が厳しすぎる場合(正当なリクエストを拒否)や緩すぎる場合(過負荷) |
| 冗長化/フェイルオーバー | SPOF をなくし、ノードやリージョンの障害に耐えます | コスト、レプリケーション遅延、フェイルオーバー時の一貫性のリスクが生じます | フェイルオーバーで未レプリケートの書き込みが失われる場合は consistency-coordination |
ストレス下での動作
このブロックは、システムが自ら障害を増幅させないようにするためのものです。
- リトライストーム: 依存先が遅くなると、すべての呼び出し元がリトライし、上流の呼び出し元によるリトライにもさらにリトライが重なって、負荷が幾何級数的に増大します。軽減策: ジッターを加えた指数バックオフ、リクエストごとのリトライ予算(試行回数の合計に上限を設定)、および停止した依存先へのリトライを完全に防ぐサーキットブレーカーを導入します。
- 復旧時のサンダリングハード: サービスが復旧すると、キューに入っていたすべてのクライアントと期限切れのキャッシュエントリが一斉にアクセスし、再びサービスを停止させます。軽減策: 少数のリクエストだけを通す半開状態のサーキットブレーカー、ジッターを加えたクライアントの再接続、リクエストの集約、スロースタートによる段階的な負荷の引き上げを行います。(キャッシュ有効期限による集中アクセスについては
cachingを参照してください。) - ヘルスチェックの集中/意図しない DDoS: 積極的すぎるヘルスチェックやロードバランサーのプローブが、復旧中のインスタンスに大量のリクエストを送ります。軽減策: プローブ間隔を緩やかにし、レディネスチェックを速やかに失敗させ、ドレインを行います。(プローブの仕組みについては
load-balancingを参照してください。) - タイムアウトがないことによる連鎖: ある依存先の遅延によってスレッドが保持され続け、プールが枯渇すると、呼び出し元も、その呼び出し元から見た「停止」状態になります。軽減策: あらゆる場所にタイムアウトとバルクヘッドを設定します。
- SPOF になるレートリミッター: 制限に使う共有カウンターストア(Redis など)が停止します。軽減策: 可用性を優先する場合はフェイルオープン(リミッターのエラー時に許可)、保護を優先する場合はフェイルクローズにします。意図的に選択してください。
監視項目: 依存先ごとのエラー率と p99、リトライ回数、サーキットブレーカーの状態遷移、プールの飽和度/キューの深さ、レート制限による拒否率、復旧後に最初の成功が得られるまでの時間。
適用方法
- 入力を明確にする — 可用性の目標、依存関係ごとの影響範囲、冪等性、レイテンシの予算、レート制限の対象となる要素とポリシー(「まず確認すること」の一覧)を確定します。これらへの回答が揃うまで、防御策は選びません。
- 防御策を選ぶ — 全体ではなく、依存関係ごとにトレードオフの表を確認します。すべてのリモート呼び出しに タイムアウト を設定します。冪等性がある場合に限り、ジッター付きのリトライを追加します。異常な下流サービスへの呼び出しが積み上がる場合は サーキットブレーカー を追加し、共有プールには バルクヘッド を適用します。古い応答を許容できるかどうかで、縮退運転 と フェイルクローズ のどちらにするか選びます。
- 主要な設定値を決める — タイムアウトは依存先の実測 p99 に設定します。リトライ上限(多くの場合 2~3 回)に加え、リクエスト単位の予算とジッターを設定します。ブレーカーが開く/半開きになるしきい値、バルクヘッドのプールサイズ、リミッターのレート/バースト、およびフェイルオープンとフェイルクローズのどちらにするかも決めます。
- 設計に負荷をかけてテストする — 「高負荷時の挙動」にある各増幅要因(リトライストーム、復旧時の集中、ヘルスチェックの集中、タイムアウト未設定による連鎖、SPOF となるリミッター)を再現し、それぞれに対策が講じられていることを確認します。
- 数値で規模を決める — リクエスト経路上の合成可用性を計算し(直列では可用性を乗算し、並列では 9 が増えます)、過剰なプロビジョニングをせずに目標を満たすことを確認します。(→
back-of-the-envelope。) - プロバイダーを選ぶ — 既定では汎用レシピを使います。ユーザーがクラウドを指定した場合に限り、プロバイダーファイルを参照します(「プロバイダーの選択」を参照)。
- 回答でトレードオフを示す — ここで行った選択ごとに 1 行を設けた
Decision | Solves | Worsens | Change it when…テーブルを提示します(例:選択肢、そのストア、障害時のポリシー)。最後の列には具体的な切り替え条件(数値または観測可能なシグナル)を記載します。そのうえで、最初に何が壊れるかを明示します。
推奨事項と禁止事項
推奨事項
- すべてのリモート呼び出しに、依存先の p99 に合わせたタイムアウトを設定します。
- ジッターとリトライ予算を設け、再試行がストームに発展しないようにします。
- 縮退した応答では、黙って誤った情報を返すのではなく、
stale: trueのように明示します。 - リミッターとブレーカーについて、フェイルオープンとフェイルクローズのどちらにするかを意図的に決めます。
- 設計をレジリエントとみなす前に、増幅要因を使ってストレステストを実施します。
禁止事項
- 冪等性キーなしで、冪等性のない書き込みをリトライしないでください(→
api-design)。 - プロセス内の呼び出しにブレーカーやバルクヘッドを適用しないでください。これらはネットワークをまたぐ仕組みです。
- SLA で求められていない 9 を追い求めないでください。冗長化のコストは非線形に増加します。
- 共有リミッターやカウンターストアを、保護されていない単一障害点にしないでください。
- 復旧中のインスタンスに、頻繁すぎるヘルスチェックや全面的な再接続で負荷をかけないでください。
重要な数値
タイムアウトは大まかな推測ではなく、依存先の実測 p99 に合わせます。リトライに上限(多くの場合 2~3 回)を設け、試行回数が際限なく増えないよう予算を設定します。可用性の「9」を 1 つ増やすたびに、必要な冗長化のコストは不釣り合いに増大します。目標を定める前に、可用性の 9 が実際に何をもたらすのか把握してください。合成可用性も重要です。直列のコンポーネントでは可用性を乗算します(リクエスト経路上に可用性 99.9% の依存先が 2 つある場合、約 99.8% になります)。並列の冗長コンポーネントでは 9 が増えます。レイテンシ表、9 の表、直列/並列の可用性計算については、いずれも back-of-the-envelope を参照してください。
インターフェースの概要
ここでは、2 つの契約が重要です。
- 縮退応答: 「縮退しています」という状態を、黙って誤った情報を返すのではなく、明示してください。フォールバックとシグナルを返します。たとえば
{ "data": [...], "stale": true, "source": "cache", "as_of": "2026-05-29T10:00Z" }のようにすることで、呼び出し元やクライアントが適切に対応できます。 - レート制限時の応答: HTTP
429 Too Many Requestsと標準ヘッダー(X-RateLimit-Limit、X-RateLimit-Remaining、秒単位のRetry-After)を返して拒否します。これにより、適切に動作するクライアントは、壁に向かってリトライし続けるのではなく、バックオフできます。
プロバイダーの選択
既定では、上記の汎用レシピ(レジリエンスライブラリ、トークンバケット/リーキーバケット方式のリミッター、ヘルスチェック、N+1 冗長化)を使います。ユーザーがクラウドを指定した場合は、マネージドサービスへの対応付け、クォータ/制限、プロバイダー固有のトレードオフについて、references/providers/<provider>.md を参照します。そのプロバイダーのファイルが存在しない場合は、汎用レシピを回答として提示します。
図
フォールバック経路(ゲートウェイ → プライマリのタイムアウト → キャッシュ/デフォルトへの破線矢印)やサーキットブレーカーの状態遷移を可視化するには、プラグイン内の
architecture-diagram スキルを使用してください。縮退経路は破線の矢印で描き、
失敗した依存先はエラー色で示してください。
関連する構成要素
messaging-streaming— これと組み合わせます。キューは書き込みの急増を吸収し、デッドレターキューは不正なメッセージを保持します。配信保証と DLQ の仕組みに関する所有概念はここにあります。load-balancing— ヘルスチェックと LB レベルのフェイルオーバールーティングでこれに依存します。そのプローブとここでの冗長化を組み合わせて、SPOF をなくします。consistency-coordination— フェイルオーバーの整合性への影響(レプリケーションされていない書き込みの喪失、パーティション発生時のクォーラム)はここで決定するため、所有概念はここにあります。api-design— 書き込みを安全に再試行するには、事前にその冪等性キーの契約に依存します。caching— フォールバック元としてグレースフルデグラデーションと組み合わせます。キャッシュ期限切れ時のスタンピード(ここで扱う復旧時の集中負荷とは異なります)に関する所有概念はここにあります。system-design— オーケストレーターへの入力となります。このブロックは、同オーケストレーターにおけるステップ 5 の障害モード確認です。
参考資料
references/deep-dive.md— サーキットブレーカーの状態遷移、バックオフ/ジッターの計算式、再試行予算、5 つのレート制限アルゴリズム(トークンバケット、リーキーバケット、固定/スライディングウィンドウ)と、分散カウンターおよび競合状態への対処、バルクヘッドのサイジング、SPOF 分析、フェイルオーバーモードを解説しています。レジリエンス層を詳しく設計する際にお読みください。references/providers/{generic,aws,azure,gcp,temporal}.md— 環境ごとのサービス対応表、制限、注意点を記載しています。temporal.mdでは、ワークフローのプリミティブとして、永続的な再試行/タイムアウトと Saga の補償処理を解説しています。