Build a Circuit Breaker
Difficulty: Medium · Category: Circuit Breaker · Languages: Java 17, Python 3.11, Go 1.22, Node.js 20, TypeScript 5.x
Circuit Breaker
Background
In distributed systems a single failing downstream service can bring down the entire call chain. A circuit breaker sits between a caller and a service, monitoring failures. When failures exceed a threshold the breaker "trips" — subsequent calls fail immediately instead of waiting for a timeout, giving the downstream service time to recover.
The pattern is modelled after electrical circuit breakers and has three states: CLOSED (normal), OPEN (tripped), and HALF_OPEN (recovery probe).
Your Task
Implement the CircuitBreakerContract interface. Your implementation must:
- Persist all state — failure counts, timestamps, and breaker state must be shared across instances.
- Track consecutive failures per service identifier.
- Transition to OPEN after
failureThresholdconsecutive failures. - After
recoveryTimeoutMsin OPEN, transition to HALF_OPEN and allow up tohalfOpenMaxAttemptstest calls. - In HALF_OPEN: a success → CLOSED (reset counts); a failure → OPEN (restart the timer).
- Be thread-safe: concurrent calls must not corrupt state.
Recommended infrastructure:
redis— for shared circuit state across instances.
How It Works
The platform injects your chosen infrastructure SDK(s) via constructor. For example, if you select Redis:
public class MyCircuitBreaker implements CircuitBreakerContract {
private final RedisClient redis;
public MyCircuitBreaker(RedisClient redis) {
this.redis = redis;
}
// ... implement methods using redis.get(), redis.set(), etc.
}
See the SDK Docs tab for the full API reference of your chosen system.
Interface
public interface CircuitBreakerContract {
void configure(int failureThreshold, long recoveryTimeoutMs, int halfOpenMaxAttempts);
boolean execute(String serviceId, Callable<Boolean> action);
String getState(String serviceId);
int getFailureCount(String serviceId);
long getLastFailureTimestamp(String serviceId);
void reset(String serviceId);
}
Method Details
| Method | Description |
|---|---|
configure | Sets breaker parameters. Called once before execute. |
execute | Runs the action through the breaker. Returns true on success, false on failure or rejection. |
getState | Returns "CLOSED", "OPEN", or "HALF_OPEN". |
getFailureCount | Returns the current consecutive failure count. |
getLastFailureTimestamp | Epoch-millis of the last failure, or 0. |
reset | Resets the breaker to CLOSED with zero failures. |
Constraints
failureThreshold≥ 1recoveryTimeoutMs≥ 100halfOpenMaxAttempts≥ 1serviceIdis a non-null, non-empty string- Performance target: ≥ 5 000 ops/sec in CLOSED state with p99 ≤ 10 ms
Examples
configure(3, 5000, 1) // 3 failures → open, 5 s recovery, 1 test
execute("svc", () -> true) → true // CLOSED, success
execute("svc", () -> { throw … }) → false // failure 1
execute("svc", () -> { throw … }) → false // failure 2
execute("svc", () -> { throw … }) → false // failure 3 → OPEN
getState("svc") → "OPEN"
execute("svc", () -> true) → false // rejected — action never called
// … wait 5 seconds …
getState("svc") → "HALF_OPEN"
execute("svc", () -> true) → true // test passes → CLOSED
getState("svc") → "CLOSED"
Hints
- Store per-service state as a group of fields:
{state, failureCount, lastFailureTs, openedAt}. - Use atomic operations for state transitions to avoid race conditions.
- For the time-based OPEN → HALF_OPEN check, compare
System.currentTimeMillis()againstopenedAt + recoveryTimeoutMs. - In HALF_OPEN, use an atomic counter to track how many test calls have been dispatched (not just completed).
Infrastructure your code runs against
- Redis 7.x
How your solution is graded
Your score combines functional correctness (50%), performance (30%) and code quality (20%).
- Target throughput: 5,000 operations/second
- Target p99 latency: 10 ms
- Load test concurrency: 8 concurrent clients
- Load test duration: 10 seconds
- Maximum error rate: 0.1%