retry

package
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Mar 10, 2021 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 NewClient. This function returns nil if the Config is configured for no retries.

The preferred way to use this package is by creating a middleware constructor with New:

  ctor := New(Config{
    Retries: 2,
    Interval: 10 * time.Second,
  })

  c := ctor(&http.Client{
	  // setup the client
  })

Exponential backoff with jitter is also supported. For example:

ctor := 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.

Index

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)

An important consequence of honoring the Temporary() method on errors is that transient network errors will be retried. One important example of this are DNS errors that are marked as temporary.

In all other cases, this default function returns false. Importantly, this means that context.DeadlineExceeded and context.Canceled 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.

func New

func New(cfg Config) client.Constructor

New creates a middleware constructor that decorates an HTTP client for retries. If cfg.Retries is nonpositive, the returned constructor does no decoration. This function is the primary and recommended way to implement HTTP client retry behavior.

The returned constructor will decorate http.DefaultClient if passed a nil client.

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. The middleware returned by New uses this type to perform retries.

func NewClient

func NewClient(cfg Config, next httpaux.Client) *Client

NewClient constructs a Client from a configuration. If cfg.Retries is nonpositive, this function returns nil.

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

This function is a low-level way to instantiate a client. Most users will instead create a middleware with New. This function can be used for more detailed usage.

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.

IMPORTANT: This method can return both a non-nil response and a non-nil error. To be consistent with CheckRedirect, in this case the response's Body has already been drained and closed. The *http.Response.Body field will be nil in that case.

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. This method will never return a nonpositive value.

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

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"`

	// MaxElapsedTime is the optional duration that all HTTP transactions together cannot
	// exceed.  This value includes the initial attempt with all retries.  If this duration
	// is exceeded, retry attempts will be short circuited.
	//
	// There is no default for this field.  Leaving this field unset means that retries up to
	// the Retries value  will continue unless the request's context expires.
	MaxElapsedTime time.Duration `json:"maxElapsedTime" yaml:"maxElapsedTime"`

	// 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.
	//
	// Note that the internal algorithm avoids floating point math in favor of fixed point.
	// Only (3) decimal places to the right of the Jitter value are supported, e.g. 0.25
	// is honored, but 0.623541 would be truncated to 0.624.
	//
	// 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.

func (*GetBodyError) Error

func (err *GetBodyError) Error() string

Error fulfills the error interface

type NoGetBodyError

type NoGetBodyError struct {
	// Initial is the error from the initial Do(request)
	Initial error
}

NoGetBodyError indicates that the initial attempt at an HTTP transaction required a retry but that the *http.Request had no GetBody field.

If an initial attempt succeeded (i.e. did NOT require a retry), the GetBody field is ignored. This error only occurs if retries were required.

func (*NoGetBodyError) Error

func (err *NoGetBodyError) Error() string

Error fulfills the error interface

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 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