From 2b23461eefdaec185688a5350593e4e7fa80c837 Mon Sep 17 00:00:00 2001 From: nutcas3 Date: Sat, 25 Apr 2026 06:04:00 +0300 Subject: [PATCH] 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 --- .../internal/circuitbreaker/circuitbreaker.go | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 apps/api-server/internal/circuitbreaker/circuitbreaker.go diff --git a/apps/api-server/internal/circuitbreaker/circuitbreaker.go b/apps/api-server/internal/circuitbreaker/circuitbreaker.go new file mode 100644 index 0000000..a04da7b --- /dev/null +++ b/apps/api-server/internal/circuitbreaker/circuitbreaker.go @@ -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 +}