Hone logo
Hone
Problems

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:

  1. Closed: The circuit breaker allows operations to proceed. It monitors failures.
  2. Open: The circuit breaker has detected too many failures and will reject all operations immediately without attempting to execute them.
  3. 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 doRequest fails in Closed or Half-Open, return the original error from doRequest.
    • If the circuit breaker is Open, return a specific "circuit breaker open" error.

Examples

Example 1:

  • Configuration: FailureThreshold = 3, Timeout = 1s
  • Scenario:
    1. doRequest is called, succeeds. Circuit breaker is Closed.
    2. doRequest is called, succeeds. Circuit breaker is Closed.
    3. doRequest is called, fails (returns ErrServiceUnavailable). Circuit breaker is Closed, 1 failure.
    4. doRequest is called, fails (returns ErrServiceUnavailable). Circuit breaker is Closed, 2 failures.
    5. doRequest is called, fails (returns ErrServiceUnavailable). Circuit breaker is Closed, 3 failures. Now it transitions to Open.
    6. doRequest is called. Circuit breaker is Open. Immediately returns ErrCircuitBreakerOpen.
    7. Wait for 1 second.
    8. doRequest is called. Circuit breaker is Half-Open. doRequest succeeds. Circuit breaker transitions back to Closed.

Example 2:

  • Configuration: FailureThreshold = 2, Timeout = 500ms
  • Scenario:
    1. doRequest is called, succeeds. Circuit breaker is Closed.
    2. doRequest is called, fails (returns ErrTimeout). Circuit breaker is Closed, 1 failure.
    3. doRequest is called, fails (returns ErrTimeout). Circuit breaker is Closed, 2 failures. Now it transitions to Open.
    4. doRequest is called. Circuit breaker is Open. Immediately returns ErrCircuitBreakerOpen.
    5. Wait for 500 milliseconds.
    6. doRequest is called. Circuit breaker is Half-Open. doRequest fails (returns ErrConnectionRefused). Circuit breaker transitions back to Open.
    7. doRequest is called. Circuit breaker is Open. Immediately returns ErrCircuitBreakerOpen.

Constraints

  • FailureThreshold will be a positive integer, typically between 1 and 10.
  • Timeout will be a positive duration, typically between 100ms and 5s.
  • The doRequest function 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.Mutex for 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 doRequest function 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.
Loading editor...
go