Functional Scope (In-Scope)
- Resilience State-Machine Transitions: Move between states (CLOSED, OPEN, HALF_OPEN) based on failure rates and time windows.
- Sliding Failures Window Counters: Track error rates over configurable sliding call windows using memory counters.
- Automated Self-Recovery Pools: Automatically transition from OPEN to HALF_OPEN after a wait period, sending probe requests to test downstream health.
- Graceful Fallback Handlers: Execute registered fallback suppliers to prevent user-facing errors when the circuit is OPEN.
Explicit Boundaries (Out-of-Scope)
- No Automatic Network Proxy Sidecar Injectors: Does not hook directly into physical network interfaces or construct active Istio Envoy sidecars.
- No Intelligent Dynamic Autoscaler Rules: Excludes auto-scaling cluster nodes or adjusting downstream pool allocations.
Clean reference designs demonstrating state transitions in Java and Python:
// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
import java.util.function.Supplier;
enum StateName { CLOSED, OPEN, HALF_OPEN }
class CircuitBreakerConfig {
final double failureRateThreshold; // e.g. 50.0 for 50%
final long waitDurationMs; // Cooldown in open state
final int minCallsForEvaluation; // Minimum calls in Closed before checking threshold
final int halfOpenMaxSuccesses; // Successes needed in Half-Open to close circuit
final int slidingWindowSize; // Size of sliding call outcome history
public CircuitBreakerConfig(double failureRateThreshold, long waitDurationMs,
int minCallsForEvaluation, int halfOpenMaxSuccesses, int slidingWindowSize) {
this.failureRateThreshold = failureRateThreshold;
this.waitDurationMs = waitDurationMs;
this.minCallsForEvaluation = minCallsForEvaluation;
this.halfOpenMaxSuccesses = halfOpenMaxSuccesses;
this.slidingWindowSize = slidingWindowSize;
}
}
// Outcome of a single service call
enum CallOutcome { SUCCESS, FAILURE }
// Sliding Window Counter to track outcome metrics
class SlidingWindowCounter {
private final int windowSize;
private final Deque<CallOutcome> outcomes = new LinkedList<>();
public SlidingWindowCounter(int windowSize) {
this.windowSize = windowSize;
}
public synchronized void record(CallOutcome outcome) {
if (outcomes.size() >= windowSize) {
outcomes.pollFirst();
}
outcomes.addLast(outcome);
}
public synchronized int getTotalCalls() {
return outcomes.size();
}
public synchronized double getFailureRate() {
if (outcomes.isEmpty()) return 0.0;
long failures = outcomes.stream().filter(o -> o == CallOutcome.FAILURE).count();
return (failures * 100.0) / outcomes.size();
}
public synchronized void clear() {
outcomes.clear();
}
}
// State Pattern Interfaces
interface CircuitBreakerState {
boolean allowCall(CircuitBreaker cb);
void recordSuccess(CircuitBreaker cb);
void recordFailure(CircuitBreaker cb);
StateName getName();
}
class ClosedState implements CircuitBreakerState {
private final SlidingWindowCounter counter;
public ClosedState(int windowSize) {
this.counter = new SlidingWindowCounter(windowSize);
}
@Override
public boolean allowCall(CircuitBreaker cb) {
return true;
}
@Override
public void recordSuccess(CircuitBreaker cb) {
counter.record(CallOutcome.SUCCESS);
evaluate(cb);
}
@Override
public void recordFailure(CircuitBreaker cb) {
counter.record(CallOutcome.FAILURE);
evaluate(cb);
}
private void evaluate(CircuitBreaker cb) {
CircuitBreakerConfig config = cb.getConfig();
if (counter.getTotalCalls() >= config.minCallsForEvaluation) {
double rate = counter.getFailureRate();
if (rate >= config.failureRateThreshold) {
System.out.println("[CB State Transition] Failure rate of " + String.format("%.1f", rate)
+ "% >= threshold of " + config.failureRateThreshold + "%. Tripping circuit to OPEN.");
cb.changeState(new OpenState());
}
}
}
@Override
public StateName getName() { return StateName.CLOSED; }
}
class OpenState implements CircuitBreakerState {
private final long openTimeMs;
public OpenState() {
this.openTimeMs = System.currentTimeMillis();
}
@Override
public boolean allowCall(CircuitBreaker cb) {
long elapsed = System.currentTimeMillis() - openTimeMs;
if (elapsed >= cb.getConfig().waitDurationMs) {
System.out.println("[CB State Transition] Wait duration expired. Transitioning to HALF_OPEN to probe.");
cb.changeState(new HalfOpenState());
return true;
}
return false;
}
@Override
public void recordSuccess(CircuitBreaker cb) {}
@Override
public void recordFailure(CircuitBreaker cb) {}
@Override
public StateName getName() { return StateName.OPEN; }
}
class HalfOpenState implements CircuitBreakerState {
private final AtomicInteger successes = new AtomicInteger(0);
@Override
public boolean allowCall(CircuitBreaker cb) {
return true;
}
@Override
public void recordSuccess(CircuitBreaker cb) {
int currentSuccesses = successes.incrementAndGet();
if (currentSuccesses >= cb.getConfig().halfOpenMaxSuccesses) {
System.out.println("[CB State Transition] Probes succeeded. Closing the circuit.");
cb.changeState(new ClosedState(cb.getConfig().slidingWindowSize));
}
}
@Override
public void recordFailure(CircuitBreaker cb) {
System.out.println("[CB State Transition] Probe call failed during HALF_OPEN. Tripping back to OPEN.");
cb.changeState(new OpenState());
}
@Override
public StateName getName() { return StateName.HALF_OPEN; }
}
class CircuitBreaker {
private final CircuitBreakerConfig config;
private final ReentrantLock lock = new ReentrantLock();
private volatile CircuitBreakerState state;
public CircuitBreaker(CircuitBreakerConfig config) {
this.config = config;
this.state = new ClosedState(config.slidingWindowSize);
}
public CircuitBreakerConfig getConfig() {
return config;
}
public StateName getStateName() {
return state.getName();
}
public void changeState(CircuitBreakerState newState) {
lock.lock();
try {
this.state = newState;
} finally {
lock.unlock();
}
}
public <T> T execute(Supplier<T> supplier, Supplier<T> fallback) {
if (!state.allowCall(this)) {
System.out.println("[CB Exec] Call blocked by CircuitBreaker (State: " + state.getName() + "). Executing fallback.");
return fallback.get();
}
try {
T result = supplier.get();
state.recordSuccess(this);
return result;
} catch (Exception e) {
state.recordFailure(this);
System.out.println("[CB Exec] Call raised error: " + e.getMessage() + ". Logged failure. Delegating to fallback.");
return fallback.get();
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
CircuitBreakerConfig config = new CircuitBreakerConfig(
50.0, // failureRateThreshold (50%)
1000, // waitDurationMs (1s)
4, // minCallsForEvaluation
2, // halfOpenMaxSuccesses
6 // slidingWindowSize
);
CircuitBreaker cb = new CircuitBreaker(config);
Supplier<String> successCall = () -> "Successful response";
Supplier<String> failedCall = () -> {
throw new RuntimeException("Connection Timeout");
};
Supplier<String> fallback = () -> "Fallback value";
// 1. Send normal successful traffic
System.out.println("--- Starting Normal Operations (CLOSED) ---");
for (int i = 0; i < 3; i++) {
System.out.println("Result: " + cb.execute(successCall, fallback));
}
// 2. Trigger failures to trip the circuit
System.out.println("\n--- Sending Failures to Trip Circuit ---");
for (int i = 0; i < 4; i++) {
System.out.println("Result: " + cb.execute(failedCall, fallback));
}
// 3. Verify requests are immediately short-circuited (OPEN state)
System.out.println("\n--- Verifying short-circuiting while OPEN ---");
System.out.println("Result: " + cb.execute(successCall, fallback));
System.out.println("Current State: " + cb.getStateName());
// 4. Cooldown and check probe auto-recovery
System.out.println("\n--- Cooldown wait... ---");
Thread.sleep(1100);
System.out.println("State: " + cb.getStateName() + " - Attempting probe 1");
System.out.println("Result: " + cb.execute(successCall, fallback)); // Probe 1 success
System.out.println("State: " + cb.getStateName() + " - Attempting probe 2");
System.out.println("Result: " + cb.execute(successCall, fallback)); // Probe 2 success
System.out.println("Final Circuit State: " + cb.getStateName());
}
}