retrybp

package
v0.9.17 Latest Latest
Warning

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

Go to latest
Published: Apr 22, 2024 License: BSD-3-Clause Imports: 8 Imported by: 0

Documentation

Overview

Package retrybp integrates with https://github.com/avast/retry-go that provides some baseplate-specific logic and changes some defaults.

Example

This example demonstrates how to use retrybp to retry an operation.

const timeout = time.Millisecond * 200

// prometheus counters
var (
	errorCounter   prometheus.Counter
	successCounter prometheus.Counter
)

// TODO: use the actual type of your operation.
type resultType = int

// In real code this should be your actual operation you need to retry.
// Here we just use a placeholder for demonstration purpose.
operation := func() (resultType, error) {
	return 0, nil
}

ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
// Optional: only do this if you want to add specific, per-call retry options.
ctx = retrybp.WithOptions(
	ctx,
	// Do 3 retries, and override whatever retries is defined in retrybp.Do.
	retry.Attempts(3+1),
)

const defaultRetries = 2
var result resultType
err := retrybp.Do(
	ctx,
	func() error {
		// This is the main function call
		var err error
		result, err = operation()
		return err
	},

	// Default retry options, can be overridden by ctx passed in.
	retry.Attempts(defaultRetries+1), // retries + the first attempt
	retrybp.CappedExponentialBackoff(retrybp.CappedExponentialBackoffArgs{
		InitialDelay: time.Millisecond * 10,
		MaxJitter:    time.Millisecond * 5,
	}),
	retrybp.Filters(
		retrybp.RetryableErrorFilter, // this should always be the first filter.
		retrybp.ContextErrorFilter,
		retrybp.NetworkErrorFilter, // optional: only use this if the requests are idempotent.

		// By default, if none of the filters above can make a decision,
		// the final decision would be not to retry (see retrybp.DefaultFilterDecision),
		// here we override it to always retry instead.
		//
		// This is only for demonstration purpose.
		// in general you want to check whether an error is retry-able,
		// instead of doing this blind decision.
		func(_ error, _ retry.RetryIfFunc) bool {
			return true
		},
	),
	retry.OnRetry(func(attempts uint, err error) {
		if err != nil {
			errorCounter.Inc()
			log.Errorw(
				"operation failed",
				"err", err,
				"attempt", attempts,
			)
		} else {
			successCounter.Inc()
		}
	}),
)

// TODO: In real code, you need to check error and use the result here.
_ = err
_ = result
Output:

Index

Examples

Constants

View Source
const (
	// DefaultFilterDecision is the default decision returned by Filters at the end
	// of the filter chain.
	DefaultFilterDecision = false
)

Variables

This section is empty.

Functions

func BreakerErrorFilter added in v0.9.0

func BreakerErrorFilter(err error, next retry.RetryIfFunc) bool

BreakerErrorFilter is a Filter implementations that retries circuit-breaker errors from gobreaker/breakerbp.

It should only be used when you are using circuit breaker, and have proper backoff policy set (e.g. using retrybp.CappedExponentialBackoff).

Example

This example demonstrates how to use retrybp with breakerbp.

const timeout = time.Millisecond * 200

// TODO: use the actual type of your operation.
type resultType = int

// In real code this should be your actual operation you need to retry.
// Here we just use a placeholder for demonstration purpose.
operation := func() (resultType, error) {
	return 0, nil
}

breaker := breakerbp.NewFailureRatioBreaker(breakerbp.Config{
	// TODO: Add breaker configs.
})

ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

const defaultRetries = 2
var result resultType
err := retrybp.Do(
	ctx,
	func() error {
		// This is the main function call,
		// wrapped with breaker.Execute to apply circuit breaker on it.
		_, err := breaker.Execute(func() (interface{}, error) {
			var err error
			result, err = operation()
			return result, err
		})
		return err
	},

	// Retry options.
	retry.Attempts(defaultRetries+1), // retries + the first attempt
	retrybp.CappedExponentialBackoff(retrybp.CappedExponentialBackoffArgs{
		InitialDelay: time.Millisecond * 10,
		MaxJitter:    time.Millisecond * 5,
	}),
	retrybp.Filters(
		retrybp.RetryableErrorFilter, // this should always be the first filter.
		retrybp.BreakerErrorFilter,   // this is the filter to handle errors returned by the breaker.
		retrybp.ContextErrorFilter,
		retrybp.NetworkErrorFilter, // optional: only use this if the requests are idempotent.
	),
)

// TODO: In real code, you need to check error and use the result here.
_ = err
_ = result
Output:

func CappedExponentialBackoff added in v0.7.0

func CappedExponentialBackoff(args CappedExponentialBackoffArgs) retry.Option

CappedExponentialBackoff is an exponentially backoff delay implementation that makes sure the delays are properly capped.

func ContextErrorFilter

func ContextErrorFilter(err error, next retry.RetryIfFunc) bool

ContextErrorFilter returns false if the error is context.Cancelled or context.DeadlineExceeded, otherwise it calls the next filter in the chain.

func Do

func Do(ctx context.Context, fn func() error, defaults ...retry.Option) error

Do retries the given function using retry.Do with the default retry.Options provided and overriding them with any options set on the context via WithOptions.

The changes this has compared to retry.Do are:

1. Pulling options from the context. This allows it to be used in middleware where you are not calling Do directly but still want to be able to configure retry behavior per-call.

2. If retry.Do returns a batch of errors (retry.Error), returns an error containing all of them.

It also auto applies retry.Context with the ctx given, so that the retries will be stopped as soon as ctx is canceled. You can override this behavior by injecting a retry.Context option into ctx.

func Filters

func Filters(filters ...Filter) retry.Option

Filters returns a `retry.RetryIf` function that checks the error against the given filters and returns either the decision reached by a filter or the DefaultFilterDecision.

You should not use this with any other retry.RetryIf options as one will override the other.

func FixedDelay added in v0.7.0

func FixedDelay(delay time.Duration) retry.Option

FixedDelay is a delay option to use fixed delay between retries.

To achieve the same result via upstream retry package's API, you would need to combine retry.Delay and retry.DelayType(retry.FixedDelay), which is confusing and error-prone. As a result we provide this API to make things easier.

If you want to combine FixedDelay with a random jitter, you could use FixedDelayFunc with retry.RandomDelay, example:

retry.DelayType(retry.CombineDelay(retry.RandomDelay, retrybp.FixedDelayFunc(delay)))

func FixedDelayFunc added in v0.7.0

func FixedDelayFunc(delay time.Duration) retry.DelayTypeFunc

FixedDelayFunc is an retry.DelayTypeFunc implementation causing fixed delays.

func GetOptions

func GetOptions(ctx context.Context) (options []retry.Option, ok bool)

GetOptions returns the list of retry.Options set on the context.

func NetworkErrorFilter

func NetworkErrorFilter(err error, next retry.RetryIfFunc) bool

NetworkErrorFilter returns true if the error is a net.Error error otherwise it calls the next filter in the chain.

This filter assumes that the error is due to a problem with the specific connection that was used to make the requset and using a new connection would fix the problem, which is why it retries on every net.Error error rather than inspecting the error for more details to determine if it is appropriate to retry or not.

This should only be used for idempotent requests. You don't want to retry calls that have side effects if the network connection broke down sometime between sending and receiving since you have no way of knowing if the callee receieved and is already processing your request.

func RetryableErrorFilter added in v0.8.0

func RetryableErrorFilter(err error, next retry.RetryIfFunc) bool

RetryableErrorFilter is a Filter implementation that checks RetryableError.

If err is not an implementation of RetryableError, or if its Retryable() returns nil, it defers to the next filter. Otherwise it use the Retryable() result.

It also checks against thrift exceptions with an optional boolean field named "retryable" defined, and use that field as the decision (unset means no decision).

In addition, it also checks retry.IsRecoverable, in case retry.Unrecoverable was used instead of retrybp.Unrecoverable.

In most cases this should be the first in the filter chain, because functions could use Unrecoverable to wrap errors that would return true in other filter implementations to explicitly override those filter behaviors.

func Unrecoverable added in v0.8.0

func Unrecoverable(err error) error

Unrecoverable wraps an error and mark it as unrecoverable by implementing RetryableError and returning false on Retryable().

It's similar to retry.Unrecoverable, but properly implements error unwrapping API in go 1.13+. As a result, it's preferred over retry.Unrecoverable.

func WithOptions

func WithOptions(ctx context.Context, options ...retry.Option) context.Context

WithOptions sets the given retry.Options on the given context.

Types

type CappedExponentialBackoffArgs added in v0.7.0

type CappedExponentialBackoffArgs struct {
	// The initial delay.
	// If <=0, retry.DefaultDelay will be used.
	// If retry.DefaultDelay <= 0, 1 nanosecond will be used.
	InitialDelay time.Duration

	// The cap of InitialDelay<<n. If <=0, it will only be capped at MaxExponent.
	//
	// Please note that it doesn't cap the MaxJitter part,
	// so the actual max delay could be MaxDelay+MaxJitter.
	MaxDelay time.Duration

	// We calculate the delay before jitter by using InitialDelay<<n
	// (n being the number of retries). MaxExponent caps the n part.
	//
	// If <=0, it will be calculated based on InitialDelay to make sure that it
	// won't overflow signed int64.
	// If it's set to a value too high that would overflow,
	// it will also be adjusted automatically.
	//
	// Please note that MaxExponent doesn't limit the number of actual retries.
	// It only caps the number of retries used in delay value calculation.
	MaxExponent int

	// Max random jitter to be added to each retry delay.
	// If <=0, no random jitter will be added.
	MaxJitter time.Duration

	// When IgnoreRetryAfterError is set to false (default),
	// and the error caused the retry implements RetryAfterError,
	// and the returned RetryAfterDuration > 0,
	// it's guaranteed that the delay value is at least RetryAfterDuration + jitter.
	//
	// If the returned RetryAfterDuration conflicts with (is larger than) MaxDelay
	// or the calculated delay result from MaxExponent,
	// RetryAfterDuration takes priority.
	IgnoreRetryAfterError bool
}

CappedExponentialBackoffArgs defines the args used in CappedExponentialBackoff retry option.

All args are optional.

type Filter

type Filter func(err error, next retry.RetryIfFunc) bool

Filter is a function that is passed an error and attempts to determine if the request should be retried given the error.

Filters should only implement a single check that will generally determine whether the request should be retried or not. If it cannot make that decision on it's own, it should call the next RetryIfFunc in the chain.

If a filter is doing more than that, there's a good chance that it is doing too much.

type RetryAfterError added in v0.8.0

type RetryAfterError interface {
	error

	// If RetryAfterDuration returns a duration <= 0,
	// it's considered as not having retry-after info.
	RetryAfterDuration() time.Duration
}

RetryAfterError defines a type of errors that contain retry-after information (for example, HTTP's Retry-After header).

httpbp.ClientError is an error type that implements this interface.

type RetryableError added in v0.8.0

type RetryableError interface {
	error

	// Errors should return 0 if there's not enough information to make a
	// decision, or >0 to indicate that it's retryable, and <0 means it's not.
	Retryable() int
}

RetryableError defines an optional error interface to return retryable info.

Jump to

Keyboard shortcuts

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