Circuit Breaker 는 실패한 의존성으로 가는 호출 자체를 차단해서, 호출자의 자원이 실패한 의존성에 점유되지 않게 한다. 의존성 호출 실패가 호출자의 쓰레드와 커넥션에 누적되면, 누적된 실패가 호출자 자체의 상태까지 악화시키고, 한 의존성의 장애가 호출 체인을 따라 연쇄적으로 번진다 — 이게 cascade 다.
차단의 기준(트리거)과 회복의 방법(회복 전략)은 별개의 결정처럼 보이지만, 두 결정이 어긋나면 차단과 회복 사이에서 시스템이 진동한다. 정밀한 차단 + 단순한 회복 조합이 cycling 안티패턴의 대표 사례다.
상태 모델
Circuit Breaker 는 세 가지 상태를 갖는다.
- Closed: 정상 동작. 호출이 통과한다.
- Open: 차단 상태. 호출이 즉시 실패한다 (fail-fast).
- Half-Open: 회복 시도 상태. 제한된 호출만 통과시켜 의존성 상태를 검증한다.
전이 트리거는 Closed → Open (차단 기준), Open → Half-Open (회복 시도 기준), Half-Open → Closed/Open (회복 검증 기준) 세 지점이다. 모든 라이브러리가 이 모델을 따른다.
차단 트리거
Closed → Open 전이 기준은 세 가지가 주로 쓰인다.
실패율 기반은 슬라이딩 윈도우 내 실패율이 임계를 넘으면 차단한다. 트래픽이 안정적이고 통계적 판단이 의미 있을 때 적합하다. 윈도우가 충분히 커야 잡음에 흔들리지 않는다.
지연/슬로우 콜 기반은 응답 시간이 임계를 넘은 호출의 비율로 차단한다. 의존성이 살아있지만 느려진 상황에 대응한다. 응답이 오긴 하니까 실패율로는 보이지 않지만, 호출자의 자원이 점유되는 시간이 길어지면 결국 같은 효과다. fail-fast 가 필요한 환경에 적합하다.
카운트 기반은 연속 실패 횟수가 임계를 넘으면 차단한다. 가장 단순하고 빠른 반응이 가능하다. 트래픽이 낮아 통계적 판단이 어렵거나, 응답 시간보다 실패 자체의 발생이 더 명확한 신호일 때 우선 후보다.
트리거 선택은 실패 신호의 모양으로 정해지지만, 차단이 의도한 보호는 회복 전략이 함께 설계되어야 완성된다.
회복 전략
Open → Half-Open → Closed 전이 기준은 두 가지가 주로 쓰인다.
Timeout 기반은 일정 시간 후 자동으로 Half-Open 으로 전이한다. 시간만 보고 회복 시도를 하는 단순한 방식이다. 의존성이 자가 회복하는 패턴(일시적 GC 압력, 짧은 네트워크 단절 등)에 적합하다.
점진적 통과율은 Half-Open 에서 일부 호출만 통과시켜 성공률을 보고, 성공이 일정 수준 이상이면 Closed 로, 미달이면 Open 으로 되돌아간다. 회복을 검증하는 방식이다. 정확한 대신 구현이 복잡하고, 의존성에 작은 시험 부하를 허용해야 한다.
단순성을 가져가면 Timeout, 정확성을 가져가면 점진적이다. 두 전략은 상호 배타가 아니라 결합 가능하다 — Timeout 으로 Half-Open 진입한 뒤 통과율로 Closed/Open 을 결정하는 하이브리드가 실전에서 흔하다. 어느 쪽이 적합한지는 차단 트리거와의 쌍 안에서 결정된다.
쌍 매트릭스
차단 트리거와 회복 전략의 조합이 Circuit Breaker 의 정체성을 결정한다. 잘 맞는 조합과 그렇지 않은 조합이 있다.
| 차단 트리거 | 회복 전략 | 정합도 | 비고 |
|---|---|---|---|
| 실패율 | 점진적 통과율 | 우세 | 양쪽 모두 통계 프레임 |
| 지연/슬로우 콜 | 점진적 통과율 | 우세 | 회복 후 지연 잔존을 다시 검증 |
| 카운트 기반 | Timeout | 적합 | 양쪽 모두 단순·빠른 반응 |
| 실패율 | Timeout | 위험 | 차단 정밀도 ↔ 회복 단순성 비대칭 → cycling |
실패율 × 점진적이 우세한 이유는 양쪽 판단 모두 통계적이라 일관성을 갖기 때문이다. 차단 기준이 윈도우 내 실패율이고 회복 기준도 Half-Open 통과 성공률이면, 같은 통계 프레임으로 진입과 회복을 결정한다.
지연 × 점진적도 우세하다. 의존성이 살아있지만 느려진 상황에서는 회복 시점에 여전히 느린지를 검증해야 한다. Timeout 으로 부하 전체를 재개하면 다시 느려지자마자 Open 으로 되돌아갈 가능성이 크다. 같은 이유로 지연 × Timeout 도 위험 조합에 속한다.
카운트 × Timeout은 적합한 조합이다. 양쪽 모두 단순하고 빠른 반응을 우선하므로, 운영 단순성과 정합한다. 의존성이 자가 회복 패턴을 갖고 짧은 주기가 허용되는 환경에서 충분하다.
실패율 × Timeout은 위험하다. 차단은 통계 기반으로 신중하게 결정하면서, 회복은 검증 없이 시간만 보고 부하 전체를 재개한다. 의존성이 회복되지 않은 채 부하를 받으면 즉시 실패율이 다시 임계를 넘고 Open 으로 되돌아간다. 결과는 무의미한 cycling — Closed/Open 사이를 진동하면서 호출자는 매 주기마다 fail-fast 의 비용을 지불한다. 차단 설계와 회복 설계의 정밀도가 맞지 않을 때 이 cycling 이 나타난다.
도구 매핑
실전 도구는 양쪽의 특정 조합으로 굳어져 있다.
| 도구 | 차단 트리거 (기본) | 회복 전략 (기본) | 굳어진 이유 |
|---|---|---|---|
| Resilience4j (Java) | 실패율 + 슬로우 콜 | 점진적 통과율 | 비즈니스 단위 보호의 통계 정밀도 |
| Polly v8 (.NET) | 실패율 (FailureRatio) | Timeout (BreakDuration) | .NET resilience 표준 통합 |
| Istio / Envoy | 카운트 기반 (연속 5xx) | Timeout (ejection time) | 사이드카는 비즈니스 컨텍스트 없음 |
Resilience4j 가 실패율 + 점진적 조합을 기본으로 한 이유는 비즈니스 로직 단위 보호에서 정밀 트리거와 회복 검증이 함께 필요해서다. 실제 동작은 두 회복 전략을 결합한다 — waitDurationInOpenState 시간 후 Half-Open 으로 자동 전이(Timeout 기반)하고, permittedNumberOfCallsInHalfOpenState 만큼의 통과 콜의 실패율로 Closed/Open 을 결정(점진적 통과율)한다.
Polly v8 이 실패율 + Timeout 조합인 이유는 .NET resilience 의 표준 컴포넌트로 통합되면서 통계적 판단이 기본이 되었기 때문이다. FailureRatio 임계와 MinimumThroughput 으로 잡음을 거른 뒤 차단하고, BreakDuration 후 Half-Open 으로 전이한다 (v7 까지는 연속 실패 카운트 기반이었으나 v8 에서 변경됨).
Istio / Envoy 가 카운트 + Timeout 인 이유는 사이드카 환경에서 외부 호출의 가장 명확한 실패 신호가 연속 5xx 응답이어서다. 사이드카는 비즈니스 컨텍스트가 없으므로 통계 판단 대신 단순 신호로 차단한다. outlier detection 의 consecutive_5xx 와 base_ejection_time 이 그 조합을 직접 노출한다.
세 도구 모두 자기 환경에 맞는 쌍을 기본 조합으로 굳혔다. 도구 선택은 이미 굳어 있는 쌍 중에서 자신의 환경과 맞는 한 행을 고르는 일이 된다.
Bulkhead
Circuit Breaker 단독으로는 cascade 를 막지 못한다. 한 의존성에 대해 차단이 걸렸어도, 다른 의존성 호출이 같은 자원 풀(쓰레드, 커넥션)을 공유하면 이미 점유된 자원이 풀리지 않는다. 차단이 효과를 발휘하기 전에 자원 풀이 고갈될 수 있다.
Bulkhead 패턴은 의존성별로 자원을 격리해서 이 문제를 해결한다. 각 의존성에 독립된 쓰레드 풀(또는 세마포어 슬롯)을 할당하면, 한 의존성의 실패가 다른 의존성 호출의 자원에 영향을 주지 않는다. Circuit Breaker 와 Bulkhead 는 결합 패턴으로 쓰인다. 한쪽만 적용하면 cascade 차단이 불완전해진다.
결정 순서
Circuit Breaker 의 결정은 단일 결정이 아니라 한 쌍의 설계다. 먼저 차단 트리거를 신호의 모양으로 정하고(실패율 / 지연 / 카운트), 회복 전략을 차단 트리거와 쌍으로 정하고(단순성이면 Timeout, 정확성이면 점진적), 마지막에 Bulkhead 와 결합해 의존성별 자원을 격리한다.
Circuit Breaker 의 차단 트리거와 회복 전략은 분리해서 결정할 수 없다. 차단을 정밀하게 설계했다면 회복도 같은 정밀도의 검증이 필요하고, 단순한 차단이라면 회복도 단순한 주기면 충분하다. 양쪽 무게가 맞아야 Circuit Breaker 가 의도한 보호를 제공한다.
참고
- Rate Limiting — 보호 계층과 알고리즘 두 결정의 제약 관계를 다룬 직전 글.
- 광고 시스템 장애 회고 — 공유 의존성과 단일 장애점 — Circuit Breaker 와 Bulkhead 가 함께 필요했던 cascade 사례.