Hone logo
Hone
Problems

Robust Service Interaction with a Circuit Breaker in Go

Circuit breakers are a crucial pattern for building resilient distributed systems. They prevent cascading failures by temporarily stopping requests to a failing service, allowing it time to recover. This challenge asks you to implement a basic circuit breaker in Go to protect a simulated external service.

Problem Description

You are tasked with implementing a circuit breaker pattern in Go. The circuit breaker should monitor calls to a simulated external service (ExternalService) and, based on the success/failure rate, transition between three states: Closed, Open, and Half-Open.

  • Closed: Requests are allowed to pass through to the external service. The circuit breaker tracks the success and failure rates.
  • Open: Requests are immediately rejected without calling the external service. This state is entered when the failure rate exceeds a defined threshold.
  • Half-Open: After a timeout period in the Open state, the circuit breaker transitions to Half-Open. In this state, a limited number of requests are allowed to pass through to the external service. If these requests succeed, the circuit breaker transitions back to the Closed state. If they fail, it returns to the Open state.

The ExternalService is simulated by a function that randomly succeeds or fails. Your circuit breaker should wrap this service and provide a Call method that handles the circuit breaker logic.

Key Requirements:

  • Implement the three circuit breaker states: Closed, Open, and Half-Open.
  • Track the success and failure rates of calls to the external service.
  • Define thresholds for transitioning between states (failure rate threshold, timeout duration).
  • Implement a timeout mechanism for the Open state.
  • Provide a Call method that encapsulates the circuit breaker logic.
  • Handle concurrency safely.

Expected Behavior:

  • When the circuit is Closed and a call fails, the failure count should increment. If the failure rate exceeds the threshold, the circuit should transition to Open.
  • When the circuit is Open, all calls should be immediately rejected with an error.
  • After the timeout period in the Open state, the circuit should transition to Half-Open.
  • When the circuit is Half-Open, a limited number of calls should be allowed to pass through. If these calls succeed, the circuit should transition to Closed. If they fail, it should transition back to Open.
  • The circuit breaker should be thread-safe.

Edge Cases to Consider:

  • What happens if the external service never succeeds?
  • What happens if the external service always succeeds?
  • How does the circuit breaker handle concurrent calls?
  • How does the circuit breaker handle errors from the external service?

Examples

Example 1:

Input: Repeated calls to ExternalService that initially succeed, then start failing frequently.
Output: Circuit transitions from Closed -> Open after exceeding failure rate threshold. Subsequent calls return error immediately. After timeout, transitions to Half-Open, then back to Closed if a few calls succeed.
Explanation: Demonstrates the core functionality of detecting failures and transitioning states.

Example 2:

Input: ExternalService always fails.
Output: Circuit remains in Open state indefinitely.
Explanation: Shows how the circuit breaker handles a consistently failing service.

Example 3:

Input: ExternalService always succeeds.
Output: Circuit remains in Closed state indefinitely.
Explanation: Demonstrates the circuit breaker's behavior when the service is consistently healthy.

Constraints

  • Failure Rate Threshold: The failure rate threshold should be configurable (e.g., 50% - 5 out of 10 calls).
  • Timeout Duration: The timeout duration for the Open state should be configurable (e.g., 5 seconds).
  • Half-Open Test Calls: The number of test calls allowed in the Half-Open state should be configurable (e.g., 3 calls).
  • Concurrency: The circuit breaker must be thread-safe and handle concurrent calls correctly.
  • ExternalService: The ExternalService function is provided and cannot be modified. It simulates a service that randomly succeeds or fails.

Notes

  • Consider using channels and goroutines to implement the timeout mechanism.
  • Use mutexes to protect shared state (failure count, state) from race conditions.
  • The ExternalService function is defined as follows:
import "math/rand"

func ExternalService() error {
    if rand.Intn(10) < 5 { // 50% chance of failure
        return nil // Success
    }
    return fmt.Errorf("service failed") // Failure
}
  • Focus on the core circuit breaker logic. Error handling within the ExternalService is already handled.
  • Think about how to make the circuit breaker configurable and reusable.
  • Consider adding logging to track state transitions and errors.
Loading editor...
go