Circuit Breaker
Level: Advanced 60–90 minConcepts: Design PatternsStateMockingBoundariesEdge Cases
Solutions: C# | TypeScript | Python
Implement the circuit breaker pattern to protect against cascading failures when calling unreliable external services.
Requirements
A circuit breaker has three states: Closed, Open, and Half-Open.
-
Closed (normal operation)
- Requests pass through to the underlying service
- Track consecutive failures
- When failures reach the failure threshold, transition to Open
- Successes reset the failure counter
-
Open (failing fast)
- All requests fail immediately without calling the service
- After the reset timeout expires, transition to Half-Open
-
Half-Open (testing recovery)
- Allow a single request through to the service
- If it succeeds, transition to Closed and reset counters
- If it fails, transition back to Open and restart the timeout
Configuration:
failureThreshold— number of consecutive failures before opening (default: 3)resetTimeout— seconds to wait before transitioning from Open to Half-Open (default: 30)
The circuit breaker should accept an injectable clock for testability.
Test Cases
Given a circuit breaker with threshold=3 and timeout=30s:
| Step | State | Action | Service Result | Circuit Breaker Result | New State |
|---|---|---|---|---|---|
| 1 | Closed | call | success | success | Closed |
| 2 | Closed | call | failure | failure | Closed (1 fail) |
| 3 | Closed | call | failure | failure | Closed (2 fails) |
| 4 | Closed | call | failure | failure | Open (3 fails = threshold) |
| 5 | Open | call | — | fail immediately | Open |
| 6 | Open +30s | call | success | success | Closed |
| 7 | Open +30s | call | failure | failure | Open (restart timeout) |
Additional cases:
- Success in Closed state resets failure count to 0
- Multiple successes after failures keep the counter at 0
- Open state does not call the underlying service at all
- Half-Open allows exactly one request through
Bonus
- Add an
onStateChangecallback that fires when the state transitions - Add
getState()to inspect the current state - Add
getMetrics()returning success count, failure count, and rejection count - Implement a half-open quota — allow N requests through in half-open instead of just 1
- Add exponential backoff — double the reset timeout after each Open→Half-Open→Open cycle
Reference Walkthrough
Full C#, TypeScript, and Python implementations live at tddbuddy-reference-katas/circuit-breaker. Twenty scenarios across construction, Closed state behaviour, Open state fail-fast, Open → HalfOpen timeout elapse, HalfOpen probe success/failure, and end-to-end round-trips — shared across all three languages — with a Breaker aggregate, BreakerState enum, Clock collaborator, BreakerBuilder + FixedClock test doubles, and named domain exceptions (BreakerThresholdInvalidException / BreakerTimeoutInvalidException / CircuitOpenException).
- C# (.NET 8, xUnit, FluentAssertions,
Func<T>operation) — walkthrough - TypeScript (Node 20, Vitest, strict types,
() => Toperation) — walkthrough - Python (3.11, pytest,
Callable[[], T], Protocol clock) — walkthrough
This kata ships in Agent Full-Bake (F3) mode: one commit per language with the full domain design landing together. The walkthroughs read as design rationale — why execute takes a callable (the breaker needs to observe the call, not a pre-computed result), why BreakerState is a flat enum rather than Gang-of-Four state-object classes (the state machine has three states and shares fields across them — the pattern would add ceremony without encapsulation), and why the Open → HalfOpen check is lazy on the next execute rather than a background tick (same shape as memory-cache’s lazy TTL expiry — no threading, deterministic tests). The reference scopes to the twenty core scenarios; the bonus items (onStateChange callbacks, getMetrics, half-open quota > 1, exponential backoff) are deliberately out of scope — each has a natural insertion point in the design without reshaping it. See the repo’s Gears section for when middle gear is the right call.