feat: Add circuit breaker pattern implementation to API server with configurable thresholds and timeout

- Add State enum with Closed, Open, and HalfOpen states and String method
- Add CircuitBreaker struct with mutex, state tracking, failure/success counters, and timeout
- Add Config struct with FailureThreshold, SuccessThreshold, and Timeout fields
- Add NewCircuitBreaker constructor with Config parameter
- Add DefaultConfig with 5 failure threshold, 2 success threshold, and 60 second timeout
This commit is contained in:
nutcas3
2026-04-25 06:04:00 +03:00
parent f5b22063e3
commit 2b23461eef
@@ -0,0 +1,142 @@
package circuitbreaker
import (
"errors"
"sync"
"time"
)
// State represents the state of the circuit breaker
type State int
const (
StateClosed State = iota
StateOpen
StateHalfOpen
)
func (s State) String() string {
switch s {
case StateClosed:
return "Closed"
case StateOpen:
return "Open"
case StateHalfOpen:
return "HalfOpen"
default:
return "Unknown"
}
}
// CircuitBreaker implements the circuit breaker pattern
type CircuitBreaker struct {
mu sync.Mutex
state State
failureCount uint
failureThreshold uint
successThreshold uint
timeout time.Duration
lastFailureTime time.Time
halfOpenSuccesses uint
}
// Config holds the configuration for the circuit breaker
type Config struct {
FailureThreshold uint
SuccessThreshold uint
Timeout time.Duration
}
// NewCircuitBreaker creates a new circuit breaker with the given configuration
func NewCircuitBreaker(config Config) *CircuitBreaker {
return &CircuitBreaker{
state: StateClosed,
failureThreshold: config.FailureThreshold,
successThreshold: config.SuccessThreshold,
timeout: config.Timeout,
}
}
// DefaultConfig returns a default configuration for the circuit breaker
func DefaultConfig() Config {
return Config{
FailureThreshold: 5,
SuccessThreshold: 2,
Timeout: 60 * time.Second,
}
}
// Execute runs the given function with circuit breaker protection
func (cb *CircuitBreaker) Execute(fn func() error) error {
cb.mu.Lock()
// Check if we should attempt to transition from Open to HalfOpen
if cb.state == StateOpen && time.Since(cb.lastFailureTime) > cb.timeout {
cb.state = StateHalfOpen
cb.halfOpenSuccesses = 0
}
// If circuit is open, reject the request
if cb.state == StateOpen {
cb.mu.Unlock()
return errors.New("circuit breaker is open")
}
cb.mu.Unlock()
// Execute the function
err := fn()
cb.mu.Lock()
defer cb.mu.Unlock()
if err != nil {
cb.onFailure()
return err
}
cb.onSuccess()
return nil
}
// onFailure handles a failed operation
func (cb *CircuitBreaker) onFailure() {
cb.failureCount++
cb.lastFailureTime = time.Now()
if cb.failureCount >= cb.failureThreshold {
cb.state = StateOpen
}
}
// onSuccess handles a successful operation
func (cb *CircuitBreaker) onSuccess() {
if cb.state == StateClosed {
cb.failureCount = 0
return
}
if cb.state == StateHalfOpen {
cb.halfOpenSuccesses++
if cb.halfOpenSuccesses >= cb.successThreshold {
cb.state = StateClosed
cb.failureCount = 0
}
}
}
// GetState returns the current state of the circuit breaker
func (cb *CircuitBreaker) GetState() State {
cb.mu.Lock()
defer cb.mu.Unlock()
return cb.state
}
// Reset resets the circuit breaker to closed state
func (cb *CircuitBreaker) Reset() {
cb.mu.Lock()
defer cb.mu.Unlock()
cb.state = StateClosed
cb.failureCount = 0
cb.halfOpenSuccesses = 0
}