サーキットブレーカーパターン入門 - 失敗中の依存先を呼び続けない

11分 で読める | 2026.04.10

公式ドキュメント

サーキットブレーカーは、失敗が続く外部serviceやDBを一時的に呼ばず、早く失敗を返すpatternです。timeout待ちのrequestとresource消費が積み上がり、障害が呼び出し元へ連鎖することを抑えます。

依存先を復旧させる仕組みではありません。呼び出しを制限し、依存先と自serviceが回復する時間を作る仕組みです。

三つの状態

Circuit BreakerがClosedで通常呼出し、失敗閾値でOpenになってfail fastし、cooldown後のHalf-Openで限定試行して成功ならClosed、失敗ならOpenへ戻る図

状態動作
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と明示的な運用停止の方が予測しやすい場合があります。

障害テスト

  1. 依存先をtimeoutさせ、Openまでの時間を測る
  2. Open中に実呼び出しが止まり、fail fastするか確認する
  3. Half-Openで許可数を超えて通らないか確認する
  4. 回復後にClosedへ戻るか確認する
  5. fallbackが誤った成功や秘密情報を返さないか確認する
  6. 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を一つの契約として設計します。

参考リソース

次に読む記事

← 一覧に戻る
PR
PR
PR
PR