retry

package module
v0.0.0-...-629d521 Latest Latest
Warning

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

Go to latest
Published: Dec 25, 2020 License: MIT Imports: 6 Imported by: 0

README

Retry

Functionality to retry a function call (usually a network call) with back-off mechanism.

Overview

When external service calls is failed in our app, we as engineer want to retry it. But retry is selfish because when failures are caused by overload, retries that increase load can make chance of success worse. In the other end, server usually implements rate limitter, therefore bruteforcing retry is not a good solution. To solve this problem, we add some amount of time between retries (back-off).

Supported Back-Off Strategy

  1. Constant Back-off
    Back-off strategy with constant time in every retry attempt. Example below is back-off with 100 millisecond delay:
    attempt 0: delay 100ms
    attempt 1: delay 100ms
    attempt 2: delay 100ms
    attempt 3: delay 100ms
    attempt 4: delay 100ms
    
  2. Capped Exponential Back-Off Strategy
    Back-off strategy to multiplied back-off by a constant after each attempt, up to some maximum value. Example below is back-off with 100 millisecond base delay and 1 second maximum delay:
    attempt 0: delay 100ms
    attempt 1: delay 200ms
    attempt 2: delay 400ms
    attempt 3: delay 800ms
    attempt 4: delay 1s
    
  3. Full Jitter Back-Off Strategy
    Back-off strategy to multiplied back-off by a random delay up until current multiplied backoff. Example below is back-off with 100 millisecond base delay and 1 second maximum delay:
    attempt 0: delay 80.374688ms
    attempt 1: delay 52.725529ms
    attempt 2: delay 194.759995ms
    attempt 3: delay 86.285294ms
    attempt 4: delay 371.775302ms
    
  4. Equal Jitter Back-Off Strategy
    This back-off strategy is a timed backoff loops which keeps some of the backoff and jitter by a smaller amount. Example below is back-off with 100 millisecond base delay and 1 second maximum delay:
    attempt 0: delay 56.423945ms
    attempt 1: delay 155.708106ms
    attempt 2: delay 325.189706ms
    attempt 3: delay 735.64193ms
    attempt 4: delay 663.795241ms
    
  5. Decorrelated Back-Off Strategy
    This back-off strategy is a timed backoff loops which is similar to "Full Jitter" with increment in the maximum jitter based on the last back-off value. Example below is back-off with 100 millisecond base delay and 1 second maximum delay:
    attempt 0: delay 235.554134ms
    attempt 1: delay 630.716505ms
    attempt 2: delay 1s
    attempt 3: delay 965.507323ms
    attempt 4: delay 1s
    
  6. Truncated Exponential Back-Off Strategy
    Back-off strategy to multiplied backoff by periodically increasing delays with additional jitters. Example below is back-off with 100 millisecond base delay and 1 second maximum delay:
    attempt 0: delay 180.735148ms
    attempt 1: delay 242.153766ms
    attempt 2: delay 475.947205ms
    attempt 3: delay 839.333323ms
    

Default Configuration

Default configurations are applied if configuration is not provided.

Configuration Value Description
DefaultRetryMaxAttempts 10 maximum retry attempt allowed
defaultRetryBackoff BackoffConstant default backoff strategy
DefaultBackoffMaximumInterval 1 second maximum backoff interval cap
DefaultBackoffBaseInterval 100 millisecond base backoff interval

Unretryable Error

When we encountered with error that is not retryable, we do not want to retry to the next attempt. Imagine that we receive status code like 404 or 400 or 401, then we can assume that service will alywas return this error whenever we try.

To solve this, you can wrap an error with function Unretryable

func() error {
    return Unretryable(errors.New("some error"))
}

This will wrap the error into UnretryableError struct and will prevent retrying to the next attempt. You can still get the context of the previous error:

if errors.Is(err, context.DeadlineExceeded) {
    // ...
}

Example

Provided two ways to use the retrier. If you just want to use one retrier, you can do like this:

err := Do(yourfunction, optionalOptions)
if err != nil {
    // do something with the error
}

If you need to re-use retrier with the same configuration, you can initialize the retrier first.

retrier := NewRetrier(optionalOptions)
err := retrier.Do(yourfunction)
if err != nil {
    // do something with the error
}

If you need to limit a whole retry procedure with a context, you can use DoWithContext function or method. While waiting for next attempt, it will also listen to context cancelation. It will throw new error that will wrap the last error value.

err := DoWithContext(ctx, yourfunction, optionalOptions)
if err != nil {
    // do something with the error
}

Here is an example how to applied different back-off strategy. Function NewRand provides concurency save rand seed source.

retrier := retry.NewRetrier(
    retry.WithMaxRetryAttempts(1),
    retry.WithBackoff(
        retry.NewBackoffTruncatedExponential(
            time.Second,
            time.Millisecond,
            retry.NewRand(time.Now().UnixNano()),
        ),
    ),
)

More examples are provided in example directory.

References

Documentation

Overview

Package retry offers feature to retry a function. It offers different backoff strategies.

Retry means when the first function execution fails, then it will do retry. It will try to retry until maximum allowed attempt. There will be a delay (backoff) from one attempt to another.

When a function return unretryable error, it will not try to the next attempt.

Index

Constants

View Source
const (
	DefaultBackoffMaximumInterval = time.Millisecond * 1000
	DefaultBackoffBaseInterval    = time.Millisecond * 100
)

DefaultBackoff denotes default configuration value for Backoff

View Source
const (
	DefaultRetryMaxAttempts uint = 10
)

DefaultRetry default configuration for retrier.

Variables

This section is empty.

Functions

func Do

func Do(f func() error, options ...Option) error

Do runs f with retry. This is handy if you do not want to initialize Retrier.

func DoWithContext

func DoWithContext(ctx context.Context, f func() error, options ...Option) error

DoWithContext runs f with retry. While waiting for next attempt, it will also check context cancelation. This is handy if you do not want to initialize Retrier.

func Unretryable

func Unretryable(err error) error

Unretryable wraps an error in UnretryableError struct When you do this inside your function, it will not retry into the next attempt.

Types

type Backoff

type Backoff interface {
	Get(attempt uint) time.Duration
}

Backoff denotes backoff strategy interface

type BackoffCappedExponential

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

BackoffCappedExponential backoff strategy to multiplied backoff by a constant after each attempt, up to some maximum value.

func NewBackoffCappedExponential

func NewBackoffCappedExponential(maxInterval, baseInterval time.Duration) BackoffCappedExponential

NewBackoffCappedExponential initialize BackoffCappedExponential.

func (BackoffCappedExponential) Get

func (c BackoffCappedExponential) Get(attempt uint) time.Duration

Get returns backoff time duration based on the given attempt.

type BackoffConstant

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

BackoffConstant backoff strategy to backoff by a constant number for each attempt.

func NewBackoffConstant

func NewBackoffConstant(baseInterval time.Duration) BackoffConstant

NewBackoffConstant initialize ConstantBackoff.

func (BackoffConstant) Get

func (c BackoffConstant) Get(_ uint) time.Duration

Get returns backoff time duration based on the given attempt.

type BackoffDecorrelated

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

BackoffDecorrelated a timed backoff loops which is similar to "Full Jitter" with increment in the maximum jitter based on the last backoff value.

func NewBackoffDecorrelated

func NewBackoffDecorrelated(maxInterval, baseInterval time.Duration, randomizer Randomizer) *BackoffDecorrelated

NewBackoffDecorrelated initialize BackoffDecorrelated.

func (*BackoffDecorrelated) Get

Get returns backoff time duration based on the given attempt.

type BackoffEqualJitter

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

BackoffEqualJitter a timed backoff loops which keeps some of the backoff and jitter by a smaller amount.

func NewBackoffEqualJitter

func NewBackoffEqualJitter(maxInterval, baseInterval time.Duration, randomizer Randomizer) BackoffEqualJitter

NewBackoffEqualJitter initialize BackoffEqualJitter.

func (BackoffEqualJitter) Get

func (c BackoffEqualJitter) Get(attempt uint) time.Duration

Get returns backoff time duration based on the given attempt.

type BackoffFullJitter

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

BackoffFullJitter backoff strategy to multiplied backoff by randomize delay up until current multiplied backoff.

func NewBackoffFullJitter

func NewBackoffFullJitter(maxInterval, baseInterval time.Duration, randomizer Randomizer) BackoffFullJitter

NewBackoffFullJitter initialize BackoffFullJitter.

func (BackoffFullJitter) Get

func (c BackoffFullJitter) Get(attempt uint) time.Duration

Get returns backoff time duration based on the given attempt.

type BackoffTruncatedExponential

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

BackoffTruncatedExponential backoff strategy to multiplied backoff by periodically increasing delays with additional jitters.

func NewBackoffTruncatedExponential

func NewBackoffTruncatedExponential(maxInterval, baseInterval time.Duration, randomizer Randomizer) BackoffTruncatedExponential

NewBackoffTruncatedExponential initialize BackoffTruncatedExponential.

func (BackoffTruncatedExponential) Get

Get returns backoff time duration based on the given attempt.

type ContextDoneError

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

ContextDoneError denotes an error that triggered when context is done.

func (*ContextDoneError) Unwrap

func (u *ContextDoneError) Unwrap() error

type Option

type Option func(*Retrier)

Option denotes optional configuration.

func WithBackoff

func WithBackoff(backoff Backoff) Option

WithBackoff configures backoff strategy.

func WithMaxRetryAttempts

func WithMaxRetryAttempts(maximumAttempts uint) Option

WithMaxRetryAttempts configures maximum attempt to retry.

type Rand

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

Rand is math.Rand wrapper with concurency protection.

func NewRand

func NewRand(seed int64) *Rand

NewRand initialize new Rand.

func (*Rand) Int63n

func (r *Rand) Int63n(n int64) int64

Int63n calls rand.Int63n with lock protection.

type Randomizer

type Randomizer interface {
	Int63n(int64) int64
}

Randomizer is the rand interface

type Retrier

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

Retrier denotes a retrier struct.

func NewRetrier

func NewRetrier(options ...Option) *Retrier

NewRetrier initializes Retrier struct. This is handy if you want to re-use retrier with the same configuration.

If option is not provided, it will use default configuration. Example:

retrier := retry.NewRetrier(
	retry.WithMaxRetryAttempts(1),
	retry.WithBackoff(
		retry.NewBackoffTruncatedExponential(
			time.Second,
			time.Millisecond,
			retry.NewRand(time.Now().UnixNano()),
		),
	),
)

func (*Retrier) Do

func (r *Retrier) Do(f func() error) error

Do runs f with retry.

func (*Retrier) DoWithContext

func (r *Retrier) DoWithContext(ctx context.Context, f func() error) error

DoWithContext runs f with retry. While waiting for next attempt, it will also check context cancelation.

type UnretryableError

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

UnretryableError denotes an error that should be exit immediately.

func (*UnretryableError) Unwrap

func (u *UnretryableError) Unwrap() error

Directories

Path Synopsis
examples
withcontext
Example of retry with context.
Example of retry with context.
withcustombackoff
Example of retry with custom back-off strategy by implementing Backoff interface.
Example of retry with custom back-off strategy by implementing Backoff interface.
withinitialization
Example of retry with initialization and runs it concurently.
Example of retry with initialization and runs it concurently.
withunretryable
Example of retry with unretryable error.
Example of retry with unretryable error.
Package mocks is a generated GoMock package.
Package mocks is a generated GoMock package.

Jump to

Keyboard shortcuts

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