gobreaker

A lightweight, thread-safe circuit breaker for Go that wraps any function call to stop cascading failures when a dependency starts failing.

Library
Go
vv1.0.0
3,689stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
42/100Fair
Development Activity4
Maintenance0
Community64
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
73/100Good
Architecture82
Code Quality85
Innovation58
Learning Curve65

gobreaker is a Go implementation of the circuit breaker pattern: a state machine that wraps calls to an unreliable dependency and, once failures cross a configurable threshold, starts failing fast instead of continuing to hammer the dependency. It tracks closed, half-open, and open states behind a single mutex, using a generation counter to keep in-flight requests from corrupting counts across a state transition, and exposes both a simple Execute(func) wrapper and a two-step Allow()/done() variant for call sites that can’t express work as one closure.

The project is maintained by Sony under the MIT license and ships as two Go modules in one repository: the original github.com/sony/gobreaker (pre-generics, interface{}-based) and github.com/sony/gobreaker/v2, which adds Go generics, a bucketed rolling-window counter, an IsExcluded hook for ignoring specific errors, and a DistributedCircuitBreaker that shares breaker state across processes through a pluggable store.

What You Get

  • A CircuitBreaker type with a single Execute(func() (T, error)) entry point that wraps any call
  • A TwoStepCircuitBreaker for call patterns that need to check-then-report manually via Allow()
  • Configurable trip conditions (ReadyToTrip), success classification (IsSuccessful), and state-change hooks (OnStateChange)
  • Sensible defaults out of the box: trips after 5 consecutive failures, 60s open-state timeout, 1 request allowed while half-open
  • In v2: generics, a bucketed rolling-window counter, an IsExcluded hook, and a DistributedCircuitBreaker for sharing state across replicas via a pluggable store

Common Use Cases

  • Wrapping outbound HTTP calls to a flaky third-party API so repeated failures stop hammering it and start failing fast
  • Protecting calls to a struggling database replica or cache node from queuing up timeouts across every request goroutine
  • Isolating the blast radius when one microservice dependency degrades, so it doesn’t cascade into the caller
  • Sharing one circuit-breaker’s open/closed state across multiple service replicas via v2’s DistributedCircuitBreaker

Under The Hood

Architecture gobreaker centers on a single CircuitBreaker struct guarded by one sync.Mutex, transitioning between StateClosed, StateHalfOpen, and StateOpen inside currentState()/setState(). A monotonically increasing generation counter, bumped in toNewGeneration(), is the key correctness mechanism: beforeRequest() captures the generation at call time and afterRequest() discards any result whose generation has since rolled over, so a slow in-flight call from a pre-trip state can never corrupt the freshly reset Counts after a transition. Execute() is a thin functional wrapper around this before/after pair, recovering and re-panicking on caller panics so the breaker still accounts for failures triggered by panics. TwoStepCircuitBreaker reuses the identical core by exposing beforeRequest/afterRequest as a manual Allow()/done() pair. The separate v2 Go module extends this same core with generics (CircuitBreaker[T]), a Counter interface with a bucketed rolling-window implementation for finer-grained trip decisions, and a DistributedCircuitBreaker[T] that persists SharedState through a pluggable SharedDataStore, letting multiple processes share one breaker’s state. Because every dependent — v1 callers, TwoStepCircuitBreaker, and the entire v2 line — sits directly on this same state machine, a change to the core generation/counts logic would ripple through all of them.

Tech Stack The implementation is Go-stdlib-only at runtime — sync, time, errors, fmt — with zero third-party dependencies in either the v1 module (go.mod targets Go 1.12) or the v2 module (go 1.22, built with toolchain go1.22.10 for generics). The only external dependency anywhere in the repo is github.com/stretchr/testify, used exclusively in test files for assertions. There’s no build tooling beyond the Go toolchain, no framework involved — this is a low-level primitive meant to be composed into other code — and the only runnable artifact besides the library is an example (example/http_breaker.go) wrapping http.Get in a breaker.

Code Quality Both modules ship extensive table-driven test suites using testify/assert, covering state transitions, concurrent goroutine access, generation-rollover races, and panic recovery. CI runs a matrix across two Go versions and both working directories (./ and ./v2), running golangci-lint (with gofmt, goimports, gosec, misspell enabled) and go test -v ./... before building and executing the example binary. Error handling is explicit throughout — sentinel errors (ErrOpenState, ErrTooManyRequests, and v2’s ErrNoSharedStore/ErrNoSharedState) rather than swallowed failures — and every exported symbol carries a doc comment explaining its contract.

API Design The public surface is deliberately small: construct a CircuitBreaker from a Settings struct with every field optional and sane defaults, then call Execute() around any function — that’s the entire integration cost for the common case, keeping boilerplate near zero. The closed/open/half-open pattern with a configurable ReadyToTrip predicate is the textbook circuit-breaker design, not a novel one — v1’s contribution is a clean, dependency-free, generation-safe implementation of it. v2 is where the differentiation shows: an IsExcluded hook to keep errors like context cancellation from counting as failures, a bucketed rolling-window counter for smoother trip decisions than v1’s fixed-interval reset, and a DistributedCircuitBreaker for sharing state across replicas via a pluggable store — capabilities most Go circuit-breaker libraries don’t offer out of the box.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search