resilience

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 17, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var DefaultBackoff = Backoff{
	Base:       100 * time.Millisecond,
	Max:        30 * time.Second,
	Multiplier: 2.0,
	Jitter:     true,
}

DefaultBackoff is a sensible starting point for most use cases.

View Source
var DefaultRetry = RetryConfig{
	MaxAttempts: 3,
	Backoff:     DefaultBackoff,
}

DefaultRetry is a sensible starting point.

View Source
var ErrCircuitOpen = vortex.ErrCircuitOpen

Functions

func IsRetryable added in v0.1.8

func IsRetryable(err error) bool

IsRetryable reports whether err is a RetryableError.

func Retry

func Retry(ctx context.Context, cfg RetryConfig, fn func(attempt int) error) error

Retry calls fn repeatedly until it succeeds or runs out of attempts. fn receives the current attempt number starting at 0. Only errors wrapped with Retryable() are retried — all other errors stop retrying immediately and are returned as-is.

example:

err := resilience.Retry(ctx, resilience.DefaultRetry, func(attempt int) error {
    if err := callAPI(); err != nil {
        if isTransient(err) {
            return resilience.Retryable(err) // retry this
        }
        return err // stop immediately — not retryable
    }
    return nil
})
Example
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/MostafaMagdSalama/vortex/resilience"
)

func main() {
	// A simulated API call that fails once before succeeding.
	failures := 0
	callAPI := func() error {
		if failures < 1 {
			failures++
			return errors.New("temporary network error")
		}
		return nil
	}

	// Retry executes the function until it succeeds or MaxAttempts is reached.
	// Only errors wrapped in resilience.Retryable() are retried.
	err := resilience.Retry(context.Background(), resilience.DefaultRetry, func(attempt int) error {
		fmt.Printf("Attempt %d...\n", attempt)

		if err := callAPI(); err != nil {
			// Wrap the error to tell Retry that this is a transient failure.
			return resilience.Retryable(err)
		}

		fmt.Println("Success!")
		return nil
	})

	if err != nil {
		fmt.Println("Final Error:", err)
	}

}
Output:
Attempt 0...
Attempt 1...
Success!

func Retryable added in v0.1.8

func Retryable(err error) error

Retryable wraps err to signal that the operation should be retried.

Types

type Backoff

type Backoff struct {
	Base       time.Duration
	Max        time.Duration
	Multiplier float64
	Jitter     bool
}

Backoff calculates how long to wait before the next retry.

func (Backoff) Duration

func (b Backoff) Duration(attempt int) time.Duration

Duration returns how long to wait before attempt number n.

type CircuitBreaker

type CircuitBreaker struct {
	// contains filtered or unexported fields
}

CircuitBreaker stops calling a failing service to give it time to recover.

Example
package main

import (
	"context"
	"errors"
	"fmt"
	"time"

	"github.com/MostafaMagdSalama/vortex"
	"github.com/MostafaMagdSalama/vortex/resilience"
)

func main() {
	// Create a CircuitBreaker that opens after 2 consecutive failures
	// and waits 100 milliseconds before allowing a trial request (Half-Open).
	cb := resilience.NewCircuitBreaker(2, 100*time.Millisecond)

	// A simulated failing API
	failingAPI := func(ctx context.Context) error {
		return errors.New("500 Internal Server Error")
	}

	// 1. First failure
	_ = cb.Execute(context.Background(), failingAPI)

	// 2. Second failure - The circuit now trips to "Open"
	_ = cb.Execute(context.Background(), failingAPI)

	// 3. Third request - Rejected immediately without calling the API
	err := cb.Execute(context.Background(), failingAPI)
	if errors.Is(err, vortex.ErrCircuitOpen) {
		fmt.Println("Circuit is open! Falling back to cache.")
	}

}
Output:
Circuit is open! Falling back to cache.

func NewCircuitBreaker

func NewCircuitBreaker(maxFailures int, timeout time.Duration) *CircuitBreaker

NewCircuitBreaker creates a breaker that opens after maxFailures consecutive failures and tries again after timeout.

func (*CircuitBreaker) Execute

func (cb *CircuitBreaker) Execute(ctx context.Context, fn func(ctx context.Context) error) error

Execute runs fn if the circuit allows it. ctx is passed to fn so the call can be cancelled. In half-open state only one trial request is allowed at a time.

example:

err := cb.Execute(ctx, func(ctx context.Context) error {
    return callAPI(ctx)
})
if errors.Is(err, resilience.ErrCircuitOpen) {
    // serve from cache instead
}

func (*CircuitBreaker) State added in v0.1.8

func (cb *CircuitBreaker) State() CircuitState

State returns the current state of the breaker.

func (*CircuitBreaker) Stats added in v0.1.8

func (cb *CircuitBreaker) Stats() Stats

Stats returns a snapshot of the breaker's runtime metrics.

type CircuitState added in v0.1.8

type CircuitState int

CircuitState represents the current state of a CircuitBreaker.

const (
	StateClosed   CircuitState = iota // normal — requests go through
	StateOpen                         // tripped — requests blocked
	StateHalfOpen                     // testing — one request allowed
)

func (CircuitState) String added in v0.1.8

func (s CircuitState) String() string

type RetryConfig

type RetryConfig struct {
	MaxAttempts int
	Backoff     Backoff
}

RetryConfig controls how retries behave.

type RetryableError added in v0.1.8

type RetryableError struct {
	Err error
}

RetryableError wraps an error and signals that the operation can be retried. Use Retryable() to wrap errors that should be retried and return unwrapped errors for failures that should stop retrying immediately.

func (RetryableError) Error added in v0.1.8

func (e RetryableError) Error() string

func (RetryableError) Unwrap added in v0.1.8

func (e RetryableError) Unwrap() error

type Stats added in v0.1.8

type Stats struct {
	Requests  int64        // total calls to Execute
	Failures  int64        // calls where fn returned an error
	Successes int64        // calls where fn returned nil
	Rejected  int64        // calls rejected because circuit was open
	State     CircuitState // current state of the breaker
}

Stats holds runtime metrics for a CircuitBreaker.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL