* CLI properly formats the IP addresses in runner show * LXD provider now waits for an IP address before returning on Create * Added a few mocks for testing Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
277 lines
7.3 KiB
Markdown
277 lines
7.3 KiB
Markdown
|
|
# retry
|
|
import "github.com/juju/retry"
|
|
|
|
The retry package encapsulates the mechanism around retrying commands.
|
|
|
|
The simple use is to call retry.Call with a function closure.
|
|
|
|
```go
|
|
|
|
|
|
err := retry.Call(retry.CallArgs{
|
|
Func: func() error { ... },
|
|
Attempts: 5,
|
|
Delay: time.Minute,
|
|
Clock: clock.WallClock,
|
|
})
|
|
|
|
```
|
|
|
|
The bare minimum arguments that need to be specified are:
|
|
* Func - the function to call
|
|
* Attempts - the number of times to try Func before giving up, or a negative number for unlimited attempts (`retry.UnlimitedAttempts`)
|
|
* Delay - how long to wait between each try that returns an error
|
|
* Clock - either the wall clock, or some testing clock
|
|
|
|
Any error that is returned from the `Func` is considered transient.
|
|
In order to identify some errors as fatal, pass in a function for the
|
|
`IsFatalError` CallArgs value.
|
|
|
|
In order to have the `Delay` change for each iteration, a `BackoffFunc`
|
|
needs to be set on the CallArgs. A simple doubling delay function is
|
|
provided by `DoubleDelay`.
|
|
|
|
An example of a more complex `BackoffFunc` could be a stepped function such
|
|
as:
|
|
|
|
```go
|
|
|
|
|
|
func StepDelay(last time.Duration, attempt int) time.Duration {
|
|
switch attempt{
|
|
case 1:
|
|
return time.Second
|
|
case 2:
|
|
return 5 * time.Second
|
|
case 3:
|
|
return 20 * time.Second
|
|
case 4:
|
|
return time.Minute
|
|
case 5:
|
|
return 5 * time.Minute
|
|
default:
|
|
return 2 * last
|
|
}
|
|
}
|
|
|
|
```
|
|
|
|
Consider some package `foo` that has a `TryAgainError`, which looks something
|
|
like this:
|
|
```go
|
|
|
|
|
|
type TryAgainError struct {
|
|
After time.Duration
|
|
}
|
|
|
|
```
|
|
and we create something that looks like this:
|
|
|
|
```go
|
|
|
|
|
|
type TryAgainHelper struct {
|
|
next time.Duration
|
|
}
|
|
|
|
func (h *TryAgainHelper) notify(lastError error, attempt int) {
|
|
if tryAgain, ok := lastError.(*foo.TryAgainError); ok {
|
|
h.next = tryAgain.After
|
|
} else {
|
|
h.next = 0
|
|
}
|
|
}
|
|
|
|
func (h *TryAgainHelper) next(last time.Duration) time.Duration {
|
|
if h.next != 0 {
|
|
return h.next
|
|
}
|
|
return last
|
|
}
|
|
|
|
```
|
|
|
|
Then we could do this:
|
|
```go
|
|
|
|
|
|
helper := TryAgainHelper{}
|
|
retry.Call(retry.CallArgs{
|
|
Func: func() error {
|
|
return foo.SomeFunc()
|
|
},
|
|
NotifyFunc: helper.notify,
|
|
BackoffFunc: helper.next,
|
|
Attempts: 20,
|
|
Delay: 100 * time.Millisecond,
|
|
Clock: clock.WallClock,
|
|
})
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Constants
|
|
``` go
|
|
const (
|
|
// UnlimitedAttempts can be used as a value for `Attempts` to clearly
|
|
// show to the reader that there is no limit to the number of attempts.
|
|
UnlimitedAttempts = -1
|
|
)
|
|
```
|
|
|
|
|
|
## func Call
|
|
``` go
|
|
func Call(args CallArgs) error
|
|
```
|
|
Call will repeatedly execute the Func until either the function returns no
|
|
error, the retry count is exceeded or the stop channel is closed.
|
|
|
|
|
|
## func DoubleDelay
|
|
``` go
|
|
func DoubleDelay(delay time.Duration, attempt int) time.Duration
|
|
```
|
|
DoubleDelay provides a simple function that doubles the duration passed in.
|
|
This can then be easily used as the `BackoffFunc` in the `CallArgs`
|
|
structure.
|
|
|
|
## func ExpBackoff
|
|
``` go
|
|
func ExpBackoff(minDelay, maxDelay time.Duration, exp float64, applyJitter bool) func(time.Duration, int) time.Duration {
|
|
```
|
|
ExpBackoff returns a function a which generates time.Duration values using an
|
|
exponential back-off algorithm with the specified parameters. The returned value
|
|
can then be easily used as the `BackoffFunc` in the `CallArgs` structure.
|
|
|
|
The next delay value is calculated using the following formula:
|
|
`newDelay = min(minDelay * exp^attempt, maxDelay)`
|
|
|
|
If `applyJitter` is set to `true`, the function will randomly select and return
|
|
back a value in the `[minDelay, newDelay]` range.
|
|
|
|
## func IsAttemptsExceeded
|
|
``` go
|
|
func IsAttemptsExceeded(err error) bool
|
|
```
|
|
IsAttemptsExceeded returns true if the error is the result of the `Call`
|
|
function finishing due to hitting the requested number of `Attempts`.
|
|
|
|
|
|
## func IsDurationExceeded
|
|
``` go
|
|
func IsDurationExceeded(err error) bool
|
|
```
|
|
IsDurationExceeded returns true if the error is the result of the `Call`
|
|
function finishing due to the total duration exceeding the specified
|
|
`MaxDuration` value.
|
|
|
|
|
|
## func IsRetryStopped
|
|
``` go
|
|
func IsRetryStopped(err error) bool
|
|
```
|
|
IsRetryStopped returns true if the error is the result of the `Call`
|
|
function finishing due to the stop channel being closed.
|
|
|
|
|
|
## func LastError
|
|
``` go
|
|
func LastError(err error) error
|
|
```
|
|
LastError retrieves the last error returned from `Func` before iteration
|
|
was terminated due to the attempt count being exceeded, the maximum
|
|
duration being exceeded, or the stop channel being closed.
|
|
|
|
|
|
|
|
## type CallArgs
|
|
``` go
|
|
type CallArgs struct {
|
|
// Func is the function that will be retried if it returns an error result.
|
|
Func func() error
|
|
|
|
// IsFatalError is a function that, if set, will be called for every non-
|
|
// nil error result from `Func`. If `IsFatalError` returns true, the error
|
|
// is immediately returned breaking out from any further retries.
|
|
IsFatalError func(error) bool
|
|
|
|
// NotifyFunc is a function that is called if Func fails, and the attempt
|
|
// number. The first time this function is called attempt is 1, the second
|
|
// time, attempt is 2 and so on.
|
|
NotifyFunc func(lastError error, attempt int)
|
|
|
|
// Attempts specifies the number of times Func should be retried before
|
|
// giving up and returning the `AttemptsExceeded` error. If a negative
|
|
// value is specified, the `Call` will retry forever.
|
|
Attempts int
|
|
|
|
// Delay specifies how long to wait between retries.
|
|
Delay time.Duration
|
|
|
|
// MaxDelay specifies how longest time to wait between retries. If no
|
|
// value is specified there is no maximum delay.
|
|
MaxDelay time.Duration
|
|
|
|
// MaxDuration specifies the maximum time the `Call` function should spend
|
|
// iterating over `Func`. The duration is calculated from the start of the
|
|
// `Call` function. If the next delay time would take the total duration
|
|
// of the call over MaxDuration, then a DurationExceeded error is
|
|
// returned. If no value is specified, Call will continue until the number
|
|
// of attempts is complete.
|
|
MaxDuration time.Duration
|
|
|
|
// BackoffFunc allows the caller to provide a function that alters the
|
|
// delay each time through the loop. If this function is not provided the
|
|
// delay is the same each iteration. Alternatively a function such as
|
|
// `retry.DoubleDelay` can be used that will provide an exponential
|
|
// backoff. The first time this function is called attempt is 1, the
|
|
// second time, attempt is 2 and so on.
|
|
BackoffFunc func(delay time.Duration, attempt int) time.Duration
|
|
|
|
// Clock provides the mechanism for waiting. Normal program execution is
|
|
// expected to use something like clock.WallClock, and tests can override
|
|
// this to not actually sleep in tests.
|
|
Clock clock.Clock
|
|
|
|
// Stop is a channel that can be used to indicate that the waiting should
|
|
// be interrupted. If Stop is nil, then the Call function cannot be interrupted.
|
|
// If the channel is closed prior to the Call function being executed, the
|
|
// Func is still attempted once.
|
|
Stop <-chan struct{}
|
|
}
|
|
```
|
|
CallArgs is a simple structure used to define the behaviour of the Call
|
|
function.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### func (\*CallArgs) Validate
|
|
``` go
|
|
func (args *CallArgs) Validate() error
|
|
```
|
|
Validate the values are valid. The ensures that the Func, Delay, Attempts
|
|
and Clock have been specified.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- - -
|
|
Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md)
|