Circuit Breaker With Failure Threshold

Trip a circuit breaker after consecutive failures, short-circuiting calls during an open period and probing recovery with a half-open state.

GoResilience

Code

snippet
package breaker

import (
	"errors"
	"sync"
	"time"
)

type State int

const (
	Closed State = iota
	Open
	HalfOpen
)

var ErrOpen = errors.New("circuit breaker is open")

type Breaker struct {
	mu           sync.Mutex
	state        State
	failures     int
	threshold    int
	openedAt     time.Time
	resetTimeout time.Duration
}

func (b *Breaker) Call(fn func() error) error {
	b.mu.Lock()
	if b.state == Open {
		if time.Since(b.openedAt) < b.resetTimeout {
			b.mu.Unlock()
			return ErrOpen
		}
		b.state = HalfOpen
	}
	b.mu.Unlock()

	err := fn()

	b.mu.Lock()
	defer b.mu.Unlock()
	if err != nil {
		b.failures++
		if b.failures >= b.threshold {
			b.state = Open
			b.openedAt = time.Now()
		}
		return err
	}
	b.failures = 0
	b.state = Closed
	return nil
}