91 lines
breaker/breaker.go
Circuit breaker with Closed, Open, and HalfOpen state transitions.
// Package breaker implements a circuit breaker for protecting upstream service calls.package breakerimport ( "sync" "time")
// State represents the current circuit state.type State intconst ( // Closed is the normal operating state; calls are allowed through. Closed State = iota // Open means the circuit has tripped; calls are rejected immediately.Open
// HalfOpen means one probe call is permitted to test recovery.HalfOpen
)
// Breaker tracks consecutive failures and opens the circuit when the threshold is reached.// After halfOpenDelay the circuit allows one probe; success closes it, failure reopens it.type Breaker struct {mu sync.Mutex
state State
failures int threshold intopenedAt time.Time
halfOpenDelay time.Duration
}
// New returns a Breaker that opens after threshold consecutive failures// and waits halfOpenDelay before allowing a probe.func New(threshold int, halfOpenDelay time.Duration) *Breaker { return &Breaker{threshold: threshold, halfOpenDelay: halfOpenDelay}}
// Allow reports whether a call is permitted and updates the circuit state.// In Closed state all calls are permitted.// In Open state calls are rejected; once halfOpenDelay has elapsed, the circuit// transitions to HalfOpen and permits exactly one probe.// In HalfOpen state all calls are rejected until RecordSuccess or RecordFailure is called.func (b *Breaker) Allow() bool {b.mu.Lock()
defer b.mu.Unlock() switch b.state { case Closed: return true case Open: if time.Since(b.openedAt) >= b.halfOpenDelay {b.state = HalfOpen
return true}
return false case HalfOpen: return false}
return false}
// RecordSuccess records a successful call result.// If the circuit is HalfOpen the probe succeeded: transition to Closed// and reset the consecutive failure counter to zero.func (b *Breaker) RecordSuccess() {b.mu.Lock()
defer b.mu.Unlock() if b.state == HalfOpen {b.state = Closed
}
}
// RecordFailure records a failed call result.// In Closed state, increments the failure counter and opens the circuit// when the counter reaches threshold.// In HalfOpen state, the probe failed: reopen the circuit and start a new cooldown.func (b *Breaker) RecordFailure() {b.mu.Lock()
defer b.mu.Unlock()b.failures++
if b.state == Closed && b.failures >= b.threshold {b.state = Open
b.openedAt = time.Now()
}
}
// State returns the current circuit state.func (b *Breaker) CurrentState() State {b.mu.Lock()
defer b.mu.Unlock() return b.state}