retry

package
v0.4.2 Latest Latest
Warning

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

Go to latest
Published: Jul 17, 2024 License: Apache-2.0 Imports: 9 Imported by: 4

Documentation

Overview

Package retry implements retry logic for HTTP clients.

The Client type can be used directly and instantiated with New.

client := New(Config{
  Retries: 2,
  Interval: 10 * time.Second,
}, nil) // uses http.DefaultClient

A Client can also be used as client middleware via its Then method.

client := New(Config{})
decorated := client.Then(new(http.Client))

Exponential backoff with jitter is also supported. For example:

client := New(Config{
  Retries: 2,
  Interval: 10 * time.Second,
  Multiplier: 2.0,
  Jitter: 0.2,
})

The basic formula for each time to wait before the next retry, in terms of Config fields, is:

Interval * (Multiplier^n) * randBetween[1-Jitter,1+Jitter]

where n is the 0-based retry.

See the documentation for the Config type for more details.

Deprecated: This functionality is moving to github.com/xmidt-org/retry

Index

Examples

Constants

View Source
const (
	// DefaultInterval is used when Config.Interval is nonpositive
	DefaultInterval time.Duration = 5 * time.Second
)

Variables

This section is empty.

Functions

func DefaultCheck

func DefaultCheck(r *http.Response, err error) bool

DefaultCheck is the Check predicate used if none is supplied.

This default implementation returns true under the following conditions:

  • The response is not nil and the status code is one of: http.StatusRequestTimeout http.StatusTooManyRequests http.StatusGatewayTimeout
  • The error is not nil and: supplies a "Temporary() bool" method that returns true (including any wrapped errors)

A consequence of honoring the Temporary() method on errors is that transient network errors will be retried. Examples of this include DNS errors that are marked as temporary.

In all other cases, this default function returns false. Importantly, this means that context.Canceled errors are not retryable errors when using this check function.

func DefaultTimer

func DefaultTimer(d time.Duration) (<-chan time.Time, func() bool)

DefaultTimer is the default Timer implementation. It simply delegates to time.NewTimer.

Types

type Check

type Check func(*http.Response, error) bool

Check is a predicate type used to determine if a result from http.Client.Do should be retried. Implementations should only return true if it is reasonable to retry the request. Examples of retryable situations are gateway timeouts and 429 (too many requests) responses.

For any HTTP transaction that is considered a success, implementations should return false to halt further retries.

For any HTTP transaction that failed but that shouldn't be retried, implementations should return false. An example of this is an HTTP status of 400, since that indicates that no further use of that request will succeed.

type Client

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

Client is an httpaux.Client that retries HTTP transactions. Instances can be used in (2) ways:

(1) Client.Do can execute HTTP transactions using whatever retry semantics were defined in New. If Config.Retries was nonpositive, the Client won't perform any retries.

(2) Client.Then can be used as client middleware to attach retry semantics to other HTTP clients.

Example (Defaults)
client := New(Config{}, nil) // uses http.DefaultClient
fmt.Println(client.Retries())
Output:

0
Example (ExponentialBackoff)
client := New(Config{
	Retries:    5,
	Interval:   3 * time.Minute,
	Multiplier: 1.5,
	// uses DefaultCheck
}, new(http.Client))
fmt.Println(client.Retries())
Output:

5
Example (ExponentialBackoffWithJitter)
client := New(Config{
	Retries:    5,
	Interval:   3 * time.Minute,
	Multiplier: 3.0,

	// a random window that increases with each retry
	// the window is [1-Jitter,1+Jitter], and a random
	// value in that range is multiplied by Interval*Multipler^n
	// where n is the 0-based attempt.
	Jitter: 0.2,
}, new(http.Client))
fmt.Println(client.Retries())
Output:

5
Example (Middleware)
retryClient := New(Config{
	Retries:  3,
	Interval: 1 * time.Minute,
}, nil) // will use http.DefaultClient

// this is a decorated client using the above retry semantics
_ = retryClient.Then(new(http.Client))

fmt.Println(retryClient.Retries())
Output:

3
Example (Simple)
client := New(Config{
	Retries:  2,
	Interval: 3 * time.Minute,

	// custom Check, if desired
	Check: func(r *http.Response, err error) bool {
		return err == nil && r.StatusCode != 200
	},
}, new(http.Client))
fmt.Println(client.Retries())
Output:

2

func New

func New(cfg Config, next httpaux.Client) (c *Client)

New constructs a Client from a configuration. If cfg.Retries is nonpositive, the returned client will not do retries.

The next instance is used to actually execute HTTP transactions. If next is nil, http.DefaultClient is used.

func (*Client) Do

func (c *Client) Do(original *http.Request) (*http.Response, error)

Do takes in an original *http.Request and makes an initial attempt plus a maximum number of retries in order to satisfy the request.

For each request, a State instance is put into the request's context that allows decorated clients to access information about the current attempt. Decorated code can obtain this State with a call to GetState.

func (*Client) Retries

func (c *Client) Retries() int

Retries returns the maximum number of retries this Client will attempt. This does not include the initial attempt. If nonpositive, then no retries will be attempted.

The State instance returned by GetState will reflect this same value.

func (*Client) Then added in v0.1.3

func (c *Client) Then(next httpaux.Client) httpaux.Client

Then is a client middleware that decorates another client with this instance's retry semantics. If next is nil, http.DefaultClient is decorated.

type Config

type Config struct {
	// Retries is the number of attempts to retry an HTTP transaction.  This value
	// does not include the initial attempt.  The maximum number of HTTP transactions
	// will be 1+Retries, accounting for the initial attempt.
	//
	// If this value is unset or nonpositive, no retries will be performed.  Leaving this
	// value unset will disable retries even if any other fields of this type are set.
	Retries int `json:"retries" yaml:"retries"`

	// Interval is the base duration between each retry attempt.  If nonpositive, DefaultInterval
	// is used.
	//
	// In the simple case, when Multiplier and Jitter are unset, this is the interval
	// between each retry.
	//
	// If Multiplier is set to a positive value, typically greater than 1.0, then each
	// successive retry waits an amount equal to Interval*(Multiplier^n), where n is
	// the current 0-based retry.  Although a Multiplier of less than 1.0 is allowed, it's discouraged
	// since that means each retry waits less time than the previous retry.
	//
	// If a Jitter is set, then each retry's interval is multiplied by a random number
	// in the range [1-Jitter,1+Jitter].  This is over and above any Multiplier value.
	Interval time.Duration `json:"interval" yaml:"interval"`

	// Multiplier is the factor applied to each retry interval.  If nonpositive, no multiplier
	// is used.  There is no default for this field.
	//
	// Each retry's interval is computed by (Previous*Multiplier), not including Jitter.
	// A Multiplier value of less than 1.0 will result in decreasing wait times for each retry.
	Multiplier float64 `json:"multiplier" yaml:"multiplier"`

	// Jitter is the factor used to determine random intervals.  The actual interval
	// between retries is generated by multiplying by a random number in the range
	// [1-Jitter, 1+Jitter].  That is, the Jitter is a percentage above and below the
	// base interval.
	//
	// If this field is not in the open range (0.0, 1.0), no jitter is used.
	Jitter float64 `json:"jitter" yaml:"jitter"`

	// Random is the optional source of randomness used in computing jitter.
	// If this value is omitted, math/rand.New is used to compute a source
	// of randomness with the current time as the seed.
	Random Random `json:"-" yaml:"-"`

	// Timer is the timer strategy used to creating channels for awaiting
	// until the next retry interval.  If unset, DefaultTimer is used.
	Timer Timer `json:"-" yaml:"-"`

	// Check is the predicate used to determine if the result of http.Client.Do
	// can be retried.  Even if this predicate returns true, the number of Retries
	// will not be exceeded.
	Check Check `json:"-" yaml:"-"`
}

Config is the set of configuration options for an HTTP client retry decorator

type GetBodyError

type GetBodyError struct {
	// Err is the error returned from GetBody
	Err error
}

GetBodyError indicates that http.Request.GetBody returned an error. Retries cannot continue in this case, since the original request body is unavailable.

A request may not have GetBody set. In that case, a Client will not attempt to refresh the body before each retry.

func (*GetBodyError) Error

func (err *GetBodyError) Error() string

Error fulfills the error interface.

func (*GetBodyError) Unwrap added in v0.1.3

func (err *GetBodyError) Unwrap() error

Unwrap returns the actual error returned from GetBody.

type Random

type Random interface {
	Int63n(int64) int64
}

Random is the subset of rand.Rand methods used by this package to compute jitter. *rand.Rand implements this interface.

type RandomFunc added in v0.1.3

type RandomFunc func(int64) int64

RandomFunc is a closure type that implements Random.

func (RandomFunc) Int63n added in v0.1.3

func (rf RandomFunc) Int63n(v int64) int64

Int63n satisfies the Random interface.

type State

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

State is the current state of a retry operation. Instances of this type are available in request context's during the initial attempt and any retries.

This type is never safe for concurrent access.

func GetState

func GetState(ctx context.Context) *State

GetState returns the retry State associated with the given context. Decorated code can make use of this for metrics, logging, etc.

IMPORTANT: State is not safe for concurrent access. The State instance returned by this function should never be retained.

func (*State) Attempt

func (s *State) Attempt() int

Attempt is the 0-based attempt to execute this HTTP transaction. Zero (0) means the initial attempt. Positive values indicate the retry attempt. In other words, this method returns the number of attempts that have been tried previously.

func (*State) Previous

func (s *State) Previous() (*http.Response, error)

Previous is the result of the previous attempt. The response body will not be available, but other information such as headers and the status code will be.

This method will return (nil, nil) for the first attempt, i.e. when Attempt() returns 0.

func (*State) Retries

func (s *State) Retries() int

Retries is the maximum number of retries that will be attempted Attempt will always return a number less than or equal to this value. The Retries value will never change during a sequence of attempts.

type Timer

type Timer func(time.Duration) (<-chan time.Time, func() bool)

Timer is a strategy for starting a timer with its stop function

Jump to

Keyboard shortcuts

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