Implementing a Go Circuit Breaker
Modern distributed systems rely on communication between various services. When one service becomes unhealthy, it can cascade and bring down other services. A circuit breaker pattern is a design technique that prevents an application from repeatedly trying to execute an operation that's likely to fail. This challenge asks you to implement a basic circuit breaker in Go.
Problem Description
Your task is to implement a circuit breaker that wraps around a function (simulated by a doRequest function in the examples). The circuit breaker should monitor the success and failure rate of operations and, based on predefined thresholds, decide whether to allow further operations or immediately reject them.
The circuit breaker will have three states:
- Closed: The circuit breaker allows operations to proceed. It monitors failures.
- Open: The circuit breaker has detected too many failures and will reject all operations immediately without attempting to execute them.
- Half-Open: After a timeout period in the Open state, the circuit breaker transitions to Half-Open. In this state, it allows a single operation to proceed. If this operation succeeds, the circuit breaker resets and returns to the Closed state. If it fails, it returns to the Open state for another timeout period.
Key Requirements:
- State Management: Implement the three states (Closed, Open, Half-Open) and the logic for transitioning between them.
- Failure Threshold: Define a configurable number of failures that triggers the transition from Closed to Open.
- Timeout: Define a configurable duration after which the circuit breaker transitions from Open to Half-Open.
- Request Execution: When in Closed or Half-Open, the circuit breaker should allow the wrapped function (
doRequest) to be executed. - Immediate Rejection: When in Open, the circuit breaker should immediately return an error without calling
doRequest. - Concurrency Safety: The circuit breaker must be safe for concurrent use from multiple goroutines.
- Error Handling: The circuit breaker should return errors appropriately:
- If
doRequestfails in Closed or Half-Open, return the original error fromdoRequest. - If the circuit breaker is Open, return a specific "circuit breaker open" error.
- If
Examples
Example 1:
- Configuration:
FailureThreshold = 3,Timeout = 1s - Scenario:
doRequestis called, succeeds. Circuit breaker is Closed.doRequestis called, succeeds. Circuit breaker is Closed.doRequestis called, fails (returnsErrServiceUnavailable). Circuit breaker is Closed, 1 failure.doRequestis called, fails (returnsErrServiceUnavailable). Circuit breaker is Closed, 2 failures.doRequestis called, fails (returnsErrServiceUnavailable). Circuit breaker is Closed, 3 failures. Now it transitions to Open.doRequestis called. Circuit breaker is Open. Immediately returnsErrCircuitBreakerOpen.- Wait for 1 second.
doRequestis called. Circuit breaker is Half-Open.doRequestsucceeds. Circuit breaker transitions back to Closed.
Example 2:
- Configuration:
FailureThreshold = 2,Timeout = 500ms - Scenario:
doRequestis called, succeeds. Circuit breaker is Closed.doRequestis called, fails (returnsErrTimeout). Circuit breaker is Closed, 1 failure.doRequestis called, fails (returnsErrTimeout). Circuit breaker is Closed, 2 failures. Now it transitions to Open.doRequestis called. Circuit breaker is Open. Immediately returnsErrCircuitBreakerOpen.- Wait for 500 milliseconds.
doRequestis called. Circuit breaker is Half-Open.doRequestfails (returnsErrConnectionRefused). Circuit breaker transitions back to Open.doRequestis called. Circuit breaker is Open. Immediately returnsErrCircuitBreakerOpen.
Constraints
FailureThresholdwill be a positive integer, typically between 1 and 10.Timeoutwill be a positive duration, typically between 100ms and 5s.- The
doRequestfunction can be simulated to return an error a certain percentage of the time or based on specific inputs. - The implementation should be able to handle thousands of concurrent requests without significant performance degradation.
Notes
- Consider using Go's
sync.Mutexfor protecting shared state within the circuit breaker. - You'll need to manage the timestamp of the last failure or the opening of the circuit breaker to implement the timeout logic.
- The
doRequestfunction is a placeholder. For testing, you can create a mock function that simulates success and failure based on certain conditions. - Think about how to reset the failure count when the circuit breaker transitions from Closed to Open, or when it successfully recovers.
- You will need to define your own error types for "circuit breaker open," "service unavailable," etc., for the examples and testing.