again

package module
v1.0.5 Latest Latest
Warning

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

Go to latest
Published: Jan 18, 2023 License: MPL-2.0 Imports: 10 Imported by: 0

README

go-again

Go CodeQL Codacy Security Scan

go-again thread safely wraps a given function and executes it until it returns a nil error or exceeds the maximum number of retries. The configuration consists of the maximum number of retries, the interval, a jitter to add a randomized backoff, the timeout, and a registry to store errors that you consider temporary, hence worth a retry. The Retry method takes a context, a function, and an optional list of temporary errors as arguments. It supports cancellation from the context and a channel invoking the Cancel() function. The registry only allows you to retry a function if it returns a registered error:

    retrier := again.NewRetrier(3, time.Millisecond*10, time.Second, time.Second)
    retrier.Registry.RegisterTemporaryError("http.ErrAbortHandler", func() TemporaryError {
        return http.ErrAbortHandler
    })

    defer retrier.Registry.UnRegisterTemporaryError("http.ErrAbortHandler")
    var retryCount int
    err := retrier.Retry(context.TODO(), func() error {
        retryCount++
        if retryCount < 3 {
            return http.ErrAbortHandler
        }
        return nil
    }, "http.ErrAbortHandler")

    if err != nil {
        // handle error
    }

Should you retry regardless of the error returned, that's easy. It's enough calling the Retry function without passing a plausible set of registered error names:

    var retryCount int
    retrier := again.NewRetrier(3, time.Millisecond*10, time.Second, time.Second)
    err := retrier.Retry(context.TODO(), func() error {
        retryCount++
        if retryCount < 3 {
            return http.ErrAbortHandler
        }
        return nil
    })
    if err != nil {
        // handle error
    }

It's also possible to create a Registry with the temporary default errors: retrier.Registry.LoadDefaults(). You can extend the list with your errors by calling the RegisterTemporaryError method.

go-again is helpful in cases where you want to retry a function if it returns a temporary error, for example, when connecting to a database or a network service.

Performance

A retrier certainly adds overhead to the execution of a function. go-again is designed to produce a minimal impact on the performance of your code, keeping thread safety and flexibility. The following benchmark shows the overhead of a retrier with 5 retries, 1s interval, 10ms jitter, and 1s timeout:

go test -bench=. -benchmem -benchtime=4s . -timeout 30m
goos: darwin
goarch: amd64
pkg: github.com/hyp3rd/go-again
cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
BenchmarkRetry-16         490851          8926 ns/op        5376 B/op          1 allocs/op
PASS
ok      github.com/hyp3rd/go-again  40.390s

Installation

go get github.com/hyp3rd/go-again

Usage

For examples with cancellation, see examples. To run the examples you can leverage the Makefile:

make run example=chan
make run example=context
Retrier
package main

import (
    "fmt"
    "time"

    "github.com/hyp3rd/go-again"
)

func main() {
    // Create a new retrier.
    retrier := again.NewRetrier(5, time.Millisecond*10, time.Second, time.Second)

    // Register a temporary error.
    retrier.Registry.RegisterTemporaryError("temporary error", func() again.TemporaryError {
        return fmt.Errorf("temporary error")
    })

    // Retry a function.
    err := retrier.Retry(context.TODO(), func() error {
        // Do something here.
        return fmt.Errorf("temporary error")
    }, "temporary error")
    if err != nil {
        fmt.Println(err)
    }
}

License

The code and documentation in this project are released under Mozilla Public License 2.0.

Author

I'm a surfer, a crypto trader, and a software architect with 15 years of experience designing highly available distributed production environments and developing cloud-native apps in public and private clouds. Feel free to hook me up on LinkedIn.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewRegistry added in v1.0.1

func NewRegistry() *registry

NewRegistry creates a new registry.

Types

type Option added in v1.0.5

type Option func(*Retrier)

Option is a function type that can be used to configure the `Retrier` struct.

func WithInterval added in v1.0.5

func WithInterval(interval time.Duration) Option

WithInterval returns an option that sets the interval.

func WithJitter added in v1.0.5

func WithJitter(jitter time.Duration) Option

WithJitter returns an option that sets the jitter.

func WithMaxRetries added in v1.0.5

func WithMaxRetries(num int) Option

WithMaxRetries returns an option that sets the maximum number of retries.

func WithTimeout added in v1.0.5

func WithTimeout(timeout time.Duration) Option

WithTimeout returns an option that sets the timeout.

type Retrier

type Retrier struct {
	// MaxRetries is the maximum number of retries.
	MaxRetries int
	// Jitter is the amount of jitter to apply to the retry interval.
	Jitter time.Duration
	// Interval is the interval between retries.
	Interval time.Duration
	// Timeout is the timeout for the retry function.
	Timeout time.Duration
	// Registry is the registry for temporary errors.
	Registry *registry
	// contains filtered or unexported fields
}

Retrier is a type that retries a function until it returns a nil error or the maximum number of retries is reached.

func NewRetrier

func NewRetrier(opts ...Option) *Retrier

NewRetrier returns a new Retrier configured with the given options. If no options are provided, the default options are used. The default options are:

  • MaxRetries: 5
  • Jitter: 1 * time.Second
  • Interval: 500 * time.Millisecond
  • Timeout: 20 * time.Second

func (*Retrier) Cancel added in v1.0.4

func (r *Retrier) Cancel()

Cancel cancels the retries notifying the `Retry` function to return.

func (*Retrier) IsTemporaryError

func (r *Retrier) IsTemporaryError(err error, names ...string) bool

IsTemporaryError checks if the error is in the list of temporary errors.

func (*Retrier) Retry

func (r *Retrier) Retry(ctx context.Context, retryableFunc RetryableFunc, temporaryErrors ...string) error

Retry retries a `retryableFunc` until it returns a nil error or the maximum number of retries is reached.

  • If the maximum number of retries is reached, the function returns a `RetryError` object.
  • If the `retryableFunc` returns a nil error, the function returns nil.
  • If the `retryableFunc` returns a temporary error, the function retries the function.
  • If the `retryableFunc` returns a non-temporary error, the function returns the error.
  • If the `temporaryErrors` list is empty, the function retries the function until the maximum number of retries is reached.
  • The context is used to cancel the retries, or set a deadline if the `retryableFunc` hangs.

func (*Retrier) SetRegistry

func (r *Retrier) SetRegistry(reg *registry)

SetRegistry sets the registry for temporary errors.

type RetryError

type RetryError struct {
	MaxRetries int
	Err        error
}

RetryError is an error returned by the Retry function when the maximum number of retries is reached.

func (*RetryError) Error

func (e *RetryError) Error() string

Error returns the error message.

func (*RetryError) Unwrap

func (e *RetryError) Unwrap() error

Unwrap returns the underlying error.

type RetryableFunc added in v1.0.5

type RetryableFunc func() error

Function signature of retryable function

type TemporaryError added in v1.0.5

type TemporaryError error

TemporaryError implements the error interface.

Directories

Path Synopsis
examples
chan command
context command

Jump to

Keyboard shortcuts

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