retry

package module
v3.0.0 Latest Latest
Warning

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

Go to latest
Published: Jun 17, 2024 License: Apache-2.0 Imports: 6 Imported by: 1

README

Retry

License GitHub go.mod Go version go.dev reference Build Status Coverage Status Go Report Card

This library supports Go >= 1.22 by getting github.com/arsham/retry/v3. For older versions (Go >= 1.20) import github.com/arsham/retry/v2, or older Go versions import github.com/arsham/retry.

Retry calls your function, and if it errors it calls it again with a delay. Eventually it returns the last error or nil if one call is successful.

r := &retry.Retry{
	Attempts: 666,
	Delay:    time.Millisecond,
}
err := r.Do(func() error {
	// do some work.
	return nil
})

You can provide multiple functions:

err := r.Do(func() error {
    return nil
}, func() error {
    return nil
}}

You can use the DoContext and pass a context object to stop when the context is cancelled:

err := r.DoContext(ctx, func() error {
	return nil
})

If you want to stop retrying you can return a special error:

err := r.Do(func() error {
	if specialCase {
		return &retry.StopError{
			Err: errors.New("a special stop"),
		}
	}
	return nil
})

The standard behaviour is to delay the amount you set. You can pass any function with this signature to change the delay behaviour:

func(attempt int, delay time.Duration) time.Duration

You can also pass the retry.IncrementalDelay function that would increase the delay with a jitter to prevent Thundering herd.

r := &retry.Retry{
	Attempts: 666,
	Delay:    10 * time.Millisecond,
	Method:   retry.IncrementalDelay,
}
err := r.Do(func() error {
	if specialCase {
		return &retry.StopError{
			Err: errors.New("a special stop"),
		}
	}
	return nil
})

License

Use of this source code is governed by the Apache 2.0 license. License can be found in the LICENSE file.

Documentation

Overview

Package retry invokes a given function until it succeeds. It sleeps in between attempts based the DelayMethod. It is useful in situations that an action might succeed after a few attempt due to unavailable resources or waiting for a condition to happen.

The default DelayMethod sleeps exactly the same amount of time between attempts. You can use the IncrementalDelay method to increment the delays between attempts. It gives a jitter to the delay to prevent Thundering herd problems. If the delay is 0 in either case, it does not sleep between tries. The IncrementalDelay has a maximum delay of 1 second, but if you need a more flexible delay, you can use the IncrementalDelayMax method and give it a max delay.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func IncrementalDelay

func IncrementalDelay(attempt int, delay time.Duration) time.Duration

IncrementalDelay increases the delay between attempts up to a second. It adds a jitter to prevent Thundering herd. If the delay is 0, it always returns 0.

Example
// This setup will delay 20ms + 40ms + 80ms + 160ms, and a jitters at 5
// attempts, until on the 6th attempt that it would succeed.
r := &retry.Retry{
	Attempts: 6,
	Delay:    20 * time.Millisecond,
	Method:   retry.IncrementalDelay,
}
i := 0
err := r.Do(func() error {
	i++
	if i < r.Attempts {
		return errors.New("ignored error")
	}
	return nil
})
fmt.Println("Error:", err)
Output:

Error: <nil>

func IncrementalDelayMax

func IncrementalDelayMax(maximum time.Duration) func(int, time.Duration) time.Duration

IncrementalDelayMax returns a DelayMethod that increases the delay between attempts up to the given maximum duration. It adds a jitter to prevent Thundering herd. If the delay is 0, it always returns 0.

Example
// This setup will delay 20ms + 40ms + 50ms + 50ms, and a jitters at 5
// attempts, until on the 6th attempt that it would succeed.
r := &retry.Retry{
	Attempts: 6,
	Delay:    20 * time.Millisecond,
	Method:   retry.IncrementalDelayMax(50 * time.Millisecond),
}
i := 0
err := r.Do(func() error {
	i++
	if i < r.Attempts {
		return errors.New("ignored error")
	}
	return nil
})
fmt.Println("Error:", err)
Output:

Error: <nil>

func StandardDelay

func StandardDelay(_ int, delay time.Duration) time.Duration

StandardDelay always delays the same amount of time.

Types

type DelayMethod

type DelayMethod func(attempt int, delay time.Duration) time.Duration

DelayMethod determines how the delay behaves. The current attempt is passed on each iteration, with the delay value of the Retry object.

type Retry

type Retry struct {
	Method   DelayMethod
	Delay    time.Duration
	MaxDelay time.Duration
	Attempts int
}

Retry attempts to call a given function until it succeeds, or returns a StopError value for a certain amount of times. It will delay between calls for any errors based on the provided Method. Retry is concurrent safe and the zero value does not do anything.

func (Retry) Do

func (r Retry) Do(fn1 repeatFunc, fns ...repeatFunc) error

Do calls fn until it returns nil or a StopError. It delays and retries if the fn returns any errors or panics. The value of the returned error, or the Err of a StopError, or an error with the panic message will be returned at the last cycle.

Example
r := &retry.Retry{
	Attempts: 4,
}
err := r.Do(func() error {
	return nil
})
fmt.Println("Error:", err)
Output:

Error: <nil>
Example (Error)
r := &retry.Retry{
	Attempts: 4,
	Delay:    time.Nanosecond,
}
err := r.Do(func() error {
	return errors.New("some error")
})
fmt.Println("Error:", err)
Output:

Error: some error
Example (MultipleFuncs)
r := &retry.Retry{
	Attempts: 4,
	Delay:    time.Nanosecond,
}
err := r.Do(func() error {
	fmt.Println("Running func 1.")
	return nil
}, func() error {
	fmt.Println("Running func 2.")
	return nil
}, func() error {
	fmt.Println("Running func 3.")
	return nil
})
fmt.Println("Error:", err)
Output:

Running func 1.
Running func 2.
Running func 3.
Error: <nil>
Example (StandardMethod)
r := &retry.Retry{
	Attempts: 4,
	Delay:    time.Nanosecond,
}
i := 0
err := r.Do(func() error {
	i++
	fmt.Printf("Running iteration %d.\n", i)
	if i < 3 {
		return errors.New("ignored error")
	}
	return nil
})
fmt.Println("Error:", err)
Output:

Running iteration 1.
Running iteration 2.
Running iteration 3.
Error: <nil>
Example (Zero)
r := &retry.Retry{}
err := r.Do(func() error {
	fmt.Println("this should not happen")
	return nil
})
fmt.Println("Error:", err)
Output:

Error: <nil>

func (Retry) DoContext

func (r Retry) DoContext(ctx context.Context, fn1 repeatFunc, fns ...repeatFunc) error

DoContext calls fn until it returns nil or a StopError. It delays and retries if the fn returns any errors or panics. If the context is cancelled, it will stop iterating and returns the error reported by ctx.Err() method. The value of the returned error, or the Err of a StopError, or an error with the panic message will be returned at the last cycle.

Example
r := &retry.Retry{
	Attempts: 4,
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
err := r.DoContext(ctx, func() error {
	// we cancel early to prove a point.
	cancel()
	return nil
}, func() error {
	panic("should not have been called")
})
fmt.Println("Error:", err)
Output:

Error: context canceled

type StopError

type StopError struct {
	Err error
}

StopError causes the Do method stop trying and will return the Err. This error then is returned by the Do method.

Example
r := &retry.Retry{
	Attempts: 10,
}
i := 0
err := r.Do(func() error {
	i++
	fmt.Printf("Running iteration %d.\n", i)
	if i > 2 {
		return &retry.StopError{}
	}
	return errors.New("ignored error")
})
fmt.Println("Error:", err)
Output:

Running iteration 1.
Running iteration 2.
Running iteration 3.
Error: <nil>
Example (StopErr)
r := &retry.Retry{
	Attempts: 10,
}
i := 0
stopErr := &retry.StopError{
	Err: errors.New("this is the returned error"),
}
err := r.Do(func() error {
	i++
	if i > 2 {
		return stopErr
	}
	return errors.New("ignored error")
})
fmt.Println("Error:", err)
fmt.Println("Stopped with:", stopErr)
Output:

Error: this is the returned error
Stopped with: this is the returned error

func (StopError) Error

func (s StopError) Error() string

func (StopError) Unwrap

func (s StopError) Unwrap() error

Jump to

Keyboard shortcuts

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