Documentation
¶
Overview ¶
Package stormbreak coordinates retry pressure through shared, concurrency-safe token budgets. Operations run once without cost; every subsequent attempt must consume shared capacity before applying context-aware exponential backoff.
Index ¶
- Variables
- func AlwaysRetry(err error) bool
- func Backoff(policy Policy, attempt int) time.Duration
- func Do[T any](ctx context.Context, budget Budget, policy Policy, ...) (T, error)
- func DoVoid(ctx context.Context, budget Budget, policy Policy, ...) error
- func IsPermanent(err error) bool
- func NeverRetry(error) bool
- func Permanent(err error) error
- type AttemptEvent
- type Budget
- type BudgetEvent
- type BudgetSnapshot
- type Classifier
- type Config
- type FailureEvent
- type Hooks
- type Option
- type Policy
- type Registry
- type RetryError
- type RetryEvent
- type SuccessEvent
- type TokenBudget
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrBudgetExhausted indicates that shared retry capacity was unavailable. ErrBudgetExhausted = errors.New("stormbreak: retry budget exhausted") // ErrAttemptsExhausted indicates that every policy attempt was used. ErrAttemptsExhausted = errors.New("stormbreak: retry attempts exhausted") // ErrInvalidPolicy indicates invalid retry policy configuration. ErrInvalidPolicy = errors.New("stormbreak: invalid retry policy") // ErrInvalidBudget indicates invalid retry budget configuration. ErrInvalidBudget = errors.New("stormbreak: invalid retry budget configuration") )
Functions ¶
func Backoff ¶
Backoff computes the delay before retry number attempt. Attempt one returns BaseDelay.
func Do ¶
func Do[T any](ctx context.Context, budget Budget, policy Policy, operation func(context.Context) (T, error), opts ...Option) (T, error)
Do executes operation immediately and gates every subsequent attempt through budget.
Example ¶
package main
import (
"context"
"errors"
"fmt"
"github.com/magnexis/stormbreak"
)
func main() {
budget, _ := stormbreak.NewBudget(stormbreak.Config{Capacity: 1})
policy := stormbreak.Policy{MaxAttempts: 2, Multiplier: 1}
attempts := 0
value, err := stormbreak.Do(context.Background(), budget, policy, func(context.Context) (int, error) {
attempts++
if attempts == 1 {
return 0, errors.New("temporary failure")
}
return 42, nil
})
fmt.Println(value, err, attempts, budget.Remaining())
}
Output: 42 <nil> 2 0
func DoVoid ¶
func DoVoid(ctx context.Context, budget Budget, policy Policy, operation func(context.Context) error, options ...Option) error
DoVoid is the error-only form of Do.
func IsPermanent ¶
IsPermanent reports whether err or one of its wrapped errors was marked permanent.
func Permanent ¶
Permanent marks err as non-retryable. A nil error remains nil.
Example ¶
package main
import (
"context"
"errors"
"fmt"
"github.com/magnexis/stormbreak"
)
func main() {
budget, _ := stormbreak.NewBudget(stormbreak.Config{Capacity: 5})
attempts := 0
err := stormbreak.DoVoid(context.Background(), budget, stormbreak.DefaultPolicy(), func(context.Context) error {
attempts++
return stormbreak.Permanent(errors.New("invalid credentials"))
})
fmt.Println(stormbreak.IsPermanent(err), attempts, budget.Remaining())
}
Output: true 1 5
Types ¶
type AttemptEvent ¶
type AttemptEvent struct{ Attempt int }
AttemptEvent is emitted immediately before an operation attempt.
type Budget ¶
type Budget interface {
// Allow consumes one retry token when capacity is available.
Allow() bool
// Remaining returns the currently available retry tokens.
Remaining() int64
// Capacity returns the maximum retry tokens.
Capacity() int64
// Reset restores the budget to full capacity.
Reset()
}
Budget controls whether a retry may proceed.
type BudgetEvent ¶
BudgetEvent is emitted when a retry cannot proceed because the budget is empty.
type BudgetSnapshot ¶
type BudgetSnapshot struct {
Capacity int64
Remaining int64
RefillRate int64
RefillInterval time.Duration
Exhausted bool
}
BudgetSnapshot is an immutable view of a TokenBudget.
type Classifier ¶
Classifier reports whether an operation error is retryable.
type Config ¶
type Config struct {
// Capacity is the maximum and initial token count.
Capacity int64
// RefillRate is the number of tokens restored per refill interval.
RefillRate int64
// RefillInterval controls how frequently elapsed time restores tokens.
RefillInterval time.Duration
}
Config configures a token-based retry budget.
type FailureEvent ¶
FailureEvent is emitted when an operation attempt fails.
type Hooks ¶
type Hooks struct {
OnAttempt func(AttemptEvent)
OnRetry func(RetryEvent)
OnSuccess func(SuccessEvent)
OnFailure func(FailureEvent)
OnBudgetExhausted func(BudgetEvent)
}
Hooks contains optional synchronous observability callbacks. Hook functions should return quickly and must not block critical paths.
type Option ¶
type Option func(*options)
Option customizes a retry execution.
func WithClassifier ¶
func WithClassifier(classifier Classifier) Option
WithClassifier replaces the default retry classifier.
func WithHooks ¶
WithHooks installs synchronous, nil-safe observability hooks.
Example ¶
package main
import (
"context"
"errors"
"fmt"
"github.com/magnexis/stormbreak"
)
func main() {
budget, _ := stormbreak.NewBudget(stormbreak.Config{Capacity: 1})
policy := stormbreak.Policy{MaxAttempts: 2, Multiplier: 1}
attempts := 0
hooks := stormbreak.Hooks{
OnRetry: func(event stormbreak.RetryEvent) {
fmt.Printf("retry attempt=%d remaining=%d\n", event.Attempt, event.BudgetRemaining)
},
}
_ = stormbreak.DoVoid(context.Background(), budget, policy, func(context.Context) error {
attempts++
if attempts == 1 {
return errors.New("temporary")
}
return nil
}, stormbreak.WithHooks(hooks))
}
Output: retry attempt=2 remaining=0
func WithRandomSource ¶
WithRandomSource supplies values in [0, 1) for deterministic jitter. It is primarily useful in tests; the function must be safe for its caller's use.
type Policy ¶
type Policy struct {
// MaxAttempts includes the initial attempt.
MaxAttempts int
// BaseDelay is the delay cap before the first retry.
BaseDelay time.Duration
// MaxDelay caps exponential backoff.
MaxDelay time.Duration
// Multiplier controls exponential growth. Zero selects the default of two.
Multiplier float64
// Jitter randomizes each delay uniformly between zero and its calculated cap.
Jitter bool
}
Policy configures retry attempts and exponential backoff.
func DefaultPolicy ¶
func DefaultPolicy() Policy
DefaultPolicy returns conservative defaults suitable for network operations.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry stores independently named retry budgets. It has no global instance.
Example ¶
package main
import (
"fmt"
"github.com/magnexis/stormbreak"
)
func main() {
registry := stormbreak.NewRegistry()
_, _ = registry.Create("database", stormbreak.Config{Capacity: 10})
_, _ = registry.Create("github-api", stormbreak.Config{Capacity: 20})
fmt.Println(registry.Names())
}
Output: [database github-api]
func (*Registry) Create ¶
func (r *Registry) Create(name string, config Config) (*TokenBudget, error)
Create validates and registers a new uniquely named budget.
func (*Registry) Get ¶
func (r *Registry) Get(name string) (*TokenBudget, bool)
Get retrieves a budget by its exact name.
func (*Registry) Snapshot ¶
func (r *Registry) Snapshot() map[string]BudgetSnapshot
Snapshot returns independent snapshots of all registered budgets.
type RetryError ¶
RetryError describes why a retry operation stopped.
func (*RetryError) Error ¶
func (e *RetryError) Error() string
Error returns a summary containing the attempt count, terminal cause, and last error.
func (*RetryError) Unwrap ¶
func (e *RetryError) Unwrap() error
Unwrap exposes both the terminal cause and last operation error to errors.Is/As.
type RetryEvent ¶
RetryEvent is emitted after a retry token is consumed and before backoff.
type SuccessEvent ¶
SuccessEvent is emitted after an operation succeeds.
type TokenBudget ¶
type TokenBudget struct {
// contains filtered or unexported fields
}
TokenBudget is a concurrency-safe, lazily refilled retry budget.
func NewBudget ¶
func NewBudget(config Config) (*TokenBudget, error)
NewBudget validates config and creates a full retry budget.
func (*TokenBudget) Allow ¶
func (b *TokenBudget) Allow() bool
Allow consumes one retry token when capacity is available.
func (*TokenBudget) Capacity ¶
func (b *TokenBudget) Capacity() int64
Capacity returns the maximum token count.
func (*TokenBudget) Remaining ¶
func (b *TokenBudget) Remaining() int64
Remaining returns the current token count after applying lazy refill.
func (*TokenBudget) Reset ¶
func (b *TokenBudget) Reset()
Reset restores full capacity and restarts the refill window.
func (*TokenBudget) Snapshot ¶
func (b *TokenBudget) Snapshot() BudgetSnapshot
Snapshot returns an immutable view after applying lazy refill.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
basic
command
|
|
|
database-retry
command
|
|
|
http-client
command
|
|
|
shared-budget
command
|
|
|
worker-pool
command
|
|
|
Package httpretry provides an HTTP transport protected by a shared stormbreak budget.
|
Package httpretry provides an HTTP transport protected by a shared stormbreak budget. |