Circuit Breaker

Level: Advanced 60–90 min

Concepts: 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.

  1. 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
  2. Open (failing fast)

    • All requests fail immediately without calling the service
    • After the reset timeout expires, transition to Half-Open
  3. 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:

StepStateActionService ResultCircuit Breaker ResultNew State
1ClosedcallsuccesssuccessClosed
2ClosedcallfailurefailureClosed (1 fail)
3ClosedcallfailurefailureClosed (2 fails)
4ClosedcallfailurefailureOpen (3 fails = threshold)
5Opencallfail immediatelyOpen
6Open +30scallsuccesssuccessClosed
7Open +30scallfailurefailureOpen (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 onStateChange callback 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, () => T operation) — 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 rationalewhy 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.