サーキットブレーカーは、失敗が続く外部serviceやDBを一時的に呼ばず、早く失敗を返すpatternです。timeout待ちのrequestとresource消費が積み上がり、障害が呼び出し元へ連鎖することを抑えます。
依存先を復旧させる仕組みではありません。呼び出しを制限し、依存先と自serviceが回復する時間を作る仕組みです。
三つの状態

| 状態 | 動作 |
|---|---|
| Closed | 通常どおり呼び、最近の成功・失敗を記録する |
| Open | 呼び出さず、すぐ失敗または許可したfallbackを返す |
| Half-Open | 少数の試行だけ依存先へ通し、回復したか確認する |
Closedで一定の失敗条件を超えるとOpenへ移ります。cooldown後にHalf-Openへ移り、限定した試行が成功すればClosed、失敗すればOpenへ戻します。
Half-Openですべてのrequestを一度に通すと、回復中のserviceを再び過負荷にします。試行数や同時実行数を明示します。
timeoutが先に必要
サーキットブレーカーが失敗を数えるには、各呼び出しが有限時間で成功または失敗する必要があります。timeoutがなければ、接続が終わらないrequestがClosed状態に残り続けます。
request timeout: 2秒
failure window: 直近30秒
minimum calls: 20件
open condition: timeout/5xxが50%以上
cooldown: 15秒
half-open probes: 最大3件
数値は例であり、推奨値ではありません。通常latency、依存先のSLO、traffic量、回復時間から調整します。requestが2件しかないのに1件失敗して50%としてOpenにしないよう、minimum call数も使います。
何を失敗として数えるか
すべての非200 responseを同じに扱いません。
- timeout、connection error、依存先の
5xx:依存先障害の候補 429 Too Many Requests:過負荷として扱う場合がある。Retry-Afterも確認- 入力不正の
400:呼び出し側の問題なので、通常はbreakerを開く根拠にしない - 認可失敗の
403:設定・権限問題であり、一時障害とは限らない - 業務上の
404:正常な「対象なし」なら失敗に含めない
operationごとに分類します。同じserviceでも検索と決済では、failureの意味とfallbackが違います。
時間窓とthreshold
process起動後の累積失敗数だけを使うと、昔の失敗でOpenになったり、長時間の成功で現在の急増を薄めたりします。直近N件または直近T秒のsliding windowを使い、failure rateやslow-call rateを計算します。
open = calls >= minimumCalls
AND failedCalls / calls >= failureRateThreshold
threshold直前で状態が頻繁に揺れる場合、window、cooldown、Half-Open成功条件を見直します。breaker状態、呼び出し結果、拒否数、Half-Open試行をmetricsとして公開します。
fallbackは「何か返す」ことではない
安全なfallbackは、業務要件で古い値や機能縮退を許せる場合だけ使います。
| 操作 | fallback例 | 注意 |
|---|---|---|
| 商品のおすすめ | セクションを非表示 | 主機能を妨げない |
| 検索補助 | cache済み候補 | 古い可能性を表示する |
| 決済確定 | 原則として成功扱いしない | 二重課金・未決済を避ける |
| 認可確認 | 古い許可を返さない | fail closedが必要な場合がある |
空配列や200 OKを無条件に返すと、障害を「結果なし」と誤表示し、監視からも隠します。縮退中であることをresponse、UI、telemetryで区別します。
retryとの組み合わせ
retryは一時的な失敗をもう一度試し、circuit breakerは失敗中の呼び出しを止めます。順序と回数を誤ると、一requestが何度も失敗を発生させます。
request
-> circuit breaker
-> bounded retry with backoff + jitter
-> timeout-protected dependency call
どの層がretryするかを一つに寄せます。client、gateway、service、SDKが各3回retryすると、依存先への試行が掛け算で増えます。POSTなど副作用を持つ操作は、idempotencyなしにretryしません。
breakerの粒度
全依存先を一つのbreakerへまとめると、画像APIの障害で決済APIまで止める可能性があります。通常は次の境界を検討します。
- 依存service
- hostまたはregion
- operation
- traffic groupやtenant(必要な場合)
細かすぎると各breakerのsample数が減り、状態数と運用が増えます。障害が独立して起き、同じfallbackと回復条件を使える単位で分けます。
processごとにbreaker stateを持つ構成では、instanceごとにOpen時刻が違います。状態を中央共有すれば一貫しやすい一方、共有storeが新しい依存になります。多くの場合はlocal stateを許容し、全体のmetricsで観測します。
採用しない条件
- localな純粋計算で、遠隔障害やresource待ちがない
- message queueが再試行・dead-letter処理を担い、同期呼び出しを保護する必要がない
- trafficが少なすぎて、failure率を判断できるsampleがない
- 呼び出しを止めると、失敗するより危険な状態になる
低trafficでは単純なtimeoutと明示的な運用停止の方が予測しやすい場合があります。
障害テスト
- 依存先をtimeoutさせ、Openまでの時間を測る
- Open中に実呼び出しが止まり、fail fastするか確認する
- Half-Openで許可数を超えて通らないか確認する
- 回復後にClosedへ戻るか確認する
- fallbackが誤った成功や秘密情報を返さないか確認する
- retry総数が設計上限を超えないか確認する
状態遷移だけでなく、利用者のresponse、queue、thread/connection pool、依存先のtrafficも同時に観測します。
よくある誤解
- timeoutの代わりになる:各callのtimeoutが先に必要です。
- Openなら依存先は停止中:breakerが観測した結果であり、service全体のhealth断定ではありません。
- fallbackは必須:安全な代替値がなければ、明確な失敗を返します。
- 失敗回数だけ決めればよい:window、minimum calls、failure分類、cooldownが必要です。
- 導入すれば障害がなくなる:障害時の影響範囲を抑えるpatternです。
まとめ
Circuit breakerは、Closedで最近の結果を測り、閾値を超えたらOpenでfail fastし、Half-Openの限定試行で回復を確認します。timeout、failure分類、時間窓、操作別の粒度、安全なfallbackを一つの契約として設計します。