Implement the Circuit Breaker Pattern to build resilient systems that can handle failures gracefully. A circuit breaker monitors calls to external services and prevents cascading failures when those services become unavailable.
The circuit breaker has three states:
- Closed: Normal operation, requests pass through
- Open: Service is failing, requests are blocked and fail fast
- Half-Open: Testing if service has recovered
You'll implement a flexible circuit breaker that can wrap any function call and provide automatic failure detection and recovery.
type CircuitBreaker interface {
Call(ctx context.Context, operation func() (interface{}, error)) (interface{}, error)
GetState() State
GetMetrics() Metrics
}
type State int
const (
StateClosed State = iota
StateOpen
StateHalfOpen
)
type Metrics struct {
Requests int64
Successes int64
Failures int64
ConsecutiveFailures int64
LastFailureTime time.Time
}
func NewCircuitBreaker(config Config) CircuitBreakertype Config struct {
MaxRequests uint32 // Max requests allowed in half-open state
Interval time.Duration // Statistical window for closed state
Timeout time.Duration // Time to wait before half-open
ReadyToTrip func(Metrics) bool // Function to determine when to trip
OnStateChange func(name string, from State, to State) // State change callback
}- Closed → Open: When
ReadyToTripreturns true - Open → Half-Open: After
Timeoutduration - Half-Open → Closed: When operation succeeds
- Half-Open → Open: When operation fails
- Closed: Allow all requests, track metrics
- Open: Reject requests immediately with
ErrCircuitBreakerOpen - Half-Open: Allow up to
MaxRequests, then decide state
- Count total requests, successes, failures
- Track consecutive failures
- Record last failure time
- Reset metrics when transitioning to closed state
// Create circuit breaker for external API calls
cb := NewCircuitBreaker(Config{
MaxRequests: 3,
Interval: time.Minute,
Timeout: 30 * time.Second,
ReadyToTrip: func(m Metrics) bool {
return m.ConsecutiveFailures >= 5
},
})
// Use circuit breaker to wrap API calls
result, err := cb.Call(ctx, func() (interface{}, error) {
return httpClient.Get("https://api.example.com/data")
})Your implementation will be tested with:
- Normal Operation: Circuit remains closed for successful calls
- Failure Detection: Circuit opens after consecutive failures
- Fast Fail: Requests fail immediately when circuit is open
- Recovery Testing: Circuit transitions to half-open after timeout
- Full Recovery: Circuit closes after successful half-open requests
- Concurrent Safety: Multiple goroutines using the same circuit breaker
var (
ErrCircuitBreakerOpen = errors.New("circuit breaker is open")
ErrTooManyRequests = errors.New("too many requests in half-open state")
)- Fork the repository.
- Clone your fork to your local machine.
- Create a directory named after your GitHub username inside
challenge-20/submissions/. - Copy the
solution-template.gofile into your submission directory. - Implement the Circuit Breaker pattern with all required functionality.
- Test your solution locally by running the test file.
- Commit and push your code to your fork.
- Create a pull request to submit your solution.
Run the following command in the challenge-20/ directory:
go test -v -raceThis challenge tests your understanding of:
- Design patterns for resilience
- State management and concurrency
- Error handling strategies
- Metrics collection
- Thread-safe programming