Documentation
¶
Overview ¶
Package breaker provides a keyed circuit breaker for guarding reconcilers against remote dependencies returning transient errors.
Transport integrates it as an http.RoundTripper with one circuit per request host, surfacing every transient failure as a *Error carrying the backoff to wait. Once a host's circuit opens, requests short-circuit without reaching the network — granting a single half-open probe per backoff window — so a backlog of keys collapses to a trickle of probes instead of hammering an unhealthy dependency. The delays suit workqueue.RequeueNotBefore floors.
For non-HTTP dependencies, use a Breaker directly: gate each attempt with Allow and report its outcome with RecordSuccess or RecordFailure.
Example ¶
package main
import (
"fmt"
"time"
"chainguard.dev/driftlessaf/breaker"
)
func main() {
b := breaker.New(
breaker.WithFailureThreshold(3),
breaker.WithBaseDelay(time.Second),
breaker.WithMaxDelay(time.Minute),
)
const host = "packages.example.dev"
for range 3 {
b.RecordFailure(host)
}
ok, _ := b.Allow(host)
fmt.Println("allowed after three failures:", ok)
b.RecordSuccess(host)
ok, _ = b.Allow(host)
fmt.Println("allowed after a success:", ok)
}
Output: allowed after three failures: false allowed after a success: true
Index ¶
Examples ¶
Constants ¶
const ( // DefaultFailureThreshold is the number of consecutive failures that trips // a key's circuit open. DefaultFailureThreshold = 5 // DefaultBaseDelay is the initial backoff, doubling per consecutive failure. DefaultBaseDelay = 30 * time.Second // DefaultMaxDelay caps the backoff; it matches workqueue.MaximumRequeueFloor. DefaultMaxDelay = time.Hour )
Defaults for New.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Breaker ¶
type Breaker struct {
// contains filtered or unexported fields
}
Breaker is a keyed circuit breaker for transient failures against remote dependencies. Once a key reaches the failure threshold, Allow short-circuits with a backoff delay until a single half-open probe per backoff window succeeds. State is in-memory and per-process. Safe for concurrent use.
func (*Breaker) Allow ¶
Allow reports whether an attempt for key may proceed. When it returns false, the returned delay is the remaining cooldown.
func (*Breaker) RecordFailure ¶
RecordFailure records a failure for key and returns the backoff to wait before retrying.
func (*Breaker) RecordSuccess ¶
RecordSuccess marks key healthy, closing its circuit.
type Error ¶
type Error struct {
// Key identifies the circuit: the request's host.
Key string
// StatusCode is the transient status code received, or zero when no
// response was received (open circuit or transport error).
StatusCode int
// RetryAfter is the backoff to wait before retrying the host, suited to a
// workqueue.RequeueNotBefore floor.
RetryAfter time.Duration
// Err is the underlying transport error, if any.
Err error
}
Error is a transient failure reported by Transport: an open circuit, a transport-level error, or a transient status code (5xx/429).
type Option ¶
type Option func(*Breaker)
Option configures a Breaker.
func WithBaseDelay ¶
WithBaseDelay sets the initial backoff delay.
func WithFailureThreshold ¶
WithFailureThreshold sets the number of consecutive failures that trips a key's circuit open.
type Transport ¶
type Transport struct {
// contains filtered or unexported fields
}
Transport is an http.RoundTripper guarding requests with a per-host Breaker. Transport-level failures and transient status codes (5xx/429) count against the host's circuit; any other response (including 4xx) closes it. Once open, requests short-circuit without reaching the network until a half-open probe succeeds.
Unlike a conventional RoundTripper, transient-status responses are consumed and returned as a *Error so every failure carries its backoff.
func NewTransport ¶
func NewTransport(base http.RoundTripper, opts ...Option) *Transport
NewTransport returns a Transport wrapping base (http.DefaultTransport when nil) with a Breaker configured by opts.
Example ¶
package main
import (
"errors"
"net/http"
"time"
"chainguard.dev/driftlessaf/breaker"
)
func main() {
client := &http.Client{
Timeout: time.Minute,
Transport: breaker.NewTransport(nil, breaker.WithFailureThreshold(3)),
}
resp, err := client.Get("https://packages.example.dev/os/x86_64/APKINDEX.tar.gz")
var berr *breaker.Error
if errors.As(err, &berr) {
// Transient failure: requeue not before berr.RetryAfter.
return
}
if err == nil {
resp.Body.Close()
}
}
Output: