-
Notifications
You must be signed in to change notification settings - Fork 0
State Machine Architecture
Fuse implements an optimized, lock-free state machine based on the classic Circuit Breaker pattern documented by Michael Nygard in Release It!.
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 OPEN ≥ timeout. |
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. |
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)
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:
- When entering
OPEN, the current epoch millisecond timestamp is recorded atomically (lastStateChangeTimestamp). - When subsequent requests arrive while the breaker is
OPEN, Fuse compares:$$\text{System.currentTimeMillis}() - \text{lastStateChangeTimestamp} \ge \text{timeout.toMillis}()$$ - If true, the thread atomically transitions the state from
OPENtoHALF_OPENvia a CAS (Compare-And-Swap) operation onAtomicReference<CircuitBreakerState>. - If false, the thread immediately throws
CircuitBreakerOpenException.
All state mutations occur using lock-free primitives:
- State storage:
AtomicReference<CircuitBreakerState> - Counters:
AtomicInteger failureCountandAtomicInteger 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.
Fuse • Zero-Dependency Circuit Breaker for Java 17+ • Licensed under Apache-2.0