Skip to content

State Machine Architecture

Frody edited this page Sep 3, 2026 · 1 revision

State Machine & Lifecycle Architecture

Fuse implements an optimized, lock-free state machine based on the classic Circuit Breaker pattern documented by Michael Nygard in Release It!.


State Model

The runtime lifecycle is modeled by the CircuitBreakerState enumeration:

State Request Handling State Transition Triggers
CLOSED Pass-Through: All requests execute normally. Failures increment an atomic counter. Trips to OPEN when consecutive recorded failures ≥ failureThreshold.
OPEN Short-Circuit: Rejects all incoming requests immediately by throwing CircuitBreakerOpenException. Transitions to HALF_OPEN once the elapsed duration in OPENtimeout.
HALF_OPEN Trial Probes: Allows limited requests through to verify downstream health. Resets to CLOSED upon successThreshold consecutive successes.
Reverts immediately to OPEN if any single probe fails.

Lifecycle Sequence Diagram

sequenceDiagram
    autonumber
    actor Client
    participant CB as DefaultCircuitBreaker
    participant Service as Remote Downstream

    Note over CB: State: CLOSED (failureCount = 0)
    Client->>CB: execute(request)
    CB->>Service: invoke()
    Service-->>CB: 200 OK (Success)
    CB-->>Client: Result

    Note over CB: Downstream starts throwing 500 / Timeouts
    loop Failures < failureThreshold (e.g. 5)
        Client->>CB: execute(request)
        CB->>Service: invoke()
        Service--xCB: Exception thrown
        CB-->>Client: Exception rethrown
    end

    Note over CB: failureThreshold reached (5) -> Transitions to OPEN
    
    loop While in OPEN duration < timeout (e.g. 30s)
        Client->>CB: execute(request)
        CB-->>Client: Throws CircuitBreakerOpenException (Short-circuit, zero I/O)
    end

    Note over CB: Timeout expires (30s elapsed) -> Lazy transition to HALF_OPEN

    Client->>CB: execute(request) [Probe 1]
    CB->>Service: invoke()
    Service-->>CB: 200 OK
    CB-->>Client: Result

    Client->>CB: execute(request) [Probe 2]
    CB->>Service: invoke()
    Service-->>CB: 200 OK
    CB-->>Client: Result

    Client->>CB: execute(request) [Probe 3]
    CB->>Service: invoke()
    Service-->>CB: 200 OK
    CB-->>Client: Result

    Note over CB: successThreshold reached (3) -> Transitions to CLOSED (Recovered)
Loading

Technical Internals

1. Lazy Timeout Evaluation

Fuse avoids background scheduler threads or timer interrupts to monitor when OPEN state expires. Running dedicated timers introduces scheduler overhead and thread contention under microsecond workloads.

Instead, Fuse uses a lazy evaluation model:

  1. When entering OPEN, the current epoch millisecond timestamp is recorded atomically (lastStateChangeTimestamp).
  2. When subsequent requests arrive while the breaker is OPEN, Fuse compares: $$\text{System.currentTimeMillis}() - \text{lastStateChangeTimestamp} \ge \text{timeout.toMillis}()$$
  3. If true, the thread atomically transitions the state from OPEN to HALF_OPEN via a CAS (Compare-And-Swap) operation on AtomicReference<CircuitBreakerState>.
  4. If false, the thread immediately throws CircuitBreakerOpenException.

2. Lock-Free State Transitions

All state mutations occur using lock-free primitives:

  • State storage: AtomicReference<CircuitBreakerState>
  • Counters: AtomicInteger failureCount and AtomicInteger successCount

This guarantees that high-throughput applications running thousands of concurrent requests will never encounter thread contention, thread starvation, or priority inversion when inspecting or updating the circuit breaker.

Clone this wiki locally