package inference import ( "errors" "sync" "time" ) // ErrCircuitOpen is returned by the breaker while it is tripped, so a caller fails fast instead of // piling requests onto a sick MEGAMIND plane. var ErrCircuitOpen = errors.New("inference: circuit breaker is open") // breakerState is the classic three state machine. type breakerState int const ( stateClosed breakerState = iota // healthy: requests pass stateOpen // tripped: fail fast until cooldown elapses stateHalfOpen // probing: allow one request to test recovery ) // breaker is a small, dependency free circuit breaker. It keeps the API binary pure Go (no cgo, nothing // that disturbs the FIPS build) rather than pulling a third party breaker library. It trips after a run // of consecutive failures, fails fast while open, then lets a single probe through after a cooldown. type breaker struct { mu sync.Mutex state breakerState failures int threshold int cooldown time.Duration openedAt time.Time now func() time.Time probeIssued bool } func newBreaker(threshold int, cooldown time.Duration) *breaker { if threshold < 1 { threshold = 1 } return &breaker{state: stateClosed, threshold: threshold, cooldown: cooldown, now: time.Now} } // allow reports whether a request may proceed right now, transitioning Open to HalfOpen when the cooldown // has elapsed. It is called before each attempt. func (b *breaker) allow() bool { b.mu.Lock() defer b.mu.Unlock() switch b.state { case stateClosed: return true case stateOpen: if b.now().Sub(b.openedAt) >= b.cooldown { b.state = stateHalfOpen b.probeIssued = true return true } return false default: // half open: only one probe at a time if b.probeIssued { return false } b.probeIssued = true return true } } // record feeds the outcome of an attempt back into the breaker. func (b *breaker) record(success bool) { b.mu.Lock() defer b.mu.Unlock() if success { b.failures = 0 b.state = stateClosed b.probeIssued = false return } switch b.state { case stateHalfOpen: b.trip() default: b.failures++ if b.failures >= b.threshold { b.trip() } } } func (b *breaker) trip() { b.state = stateOpen b.openedAt = b.now() b.probeIssued = false }