Skip to content

Algorithm ​

The breaker is a finite state machine with three states. The core (CircuitBreakerState) is pure and time-injected — every time-dependent method takes an explicit now (epoch ms) — and the distributed layer applies the exact same rules atomically in Redis via Lua.

CLOSED ​

  • Requests are permitted.
  • Each failure timestamp is recorded; failures with timestamp <= now - windowMs age out (a rolling window, not a fixed reset).
  • When the number of failures still inside the window reaches failureThreshold, the breaker trips to OPEN.
  • Successes are a no-op in CLOSED — the window ages out on its own; a success does not reset it.

OPEN ​

  • Requests are rejected immediately (fail-fast); the guarded function does not run.
  • After openDurationMs has elapsed since the breaker opened, the next request commits the transition to HALF_OPEN (this flip happens on canRequest, never on a passive getState).

HALF_OPEN ​

  • Up to halfOpenMaxCalls probe calls are permitted concurrently; further calls are rejected.
  • Each permitted probe records its start time. A probe that stays unresolved longer than probeTimeoutMs is presumed dead (e.g. the caller crashed before recording an outcome): its slot is reclaimed so recovery is never blocked by a zombie probe. If such a probe eventually resolves, its outcome still counts.
  • Each successful probe increments a counter; once successThreshold probes succeed, the breaker closes and all counters clear.
  • A single probe failure re-opens the breaker with a fresh openDurationMs.

Time and determinism ​

The state machine never reads the clock itself. In the distributed store, the adapter obtains now via Date.now() and passes it into the Lua scripts as an argument — the Lua never reads time. This mirrors the rate-limit sliding-window design and keeps behaviour deterministic and testable.

Distributed storage ​

Per circuit, three Redis keys share a hash tag so they land on the same cluster slot:

  • a hash holding state, opened_at, ho_succ;
  • a sorted set of failure timestamps (pruned by the rolling window);
  • a sorted set of in-flight probe start times (pruned by probeTimeoutMs).

All transitions (canRequest, recordSuccess, recordFailure) are single atomic Lua scripts, so concurrent instances never observe a torn state.

Released under the MIT License.