retry: aqwari.net/retry Index | Examples | Files

package retry

import "aqwari.net/retry"

Package retry provides a stateless method for implementing exponential and other backoff strategies.

The retry package defines a Strategy as a function that maps an integer to a time.Duration. Users of the retry package are expected to maintain a counter variable that represents the number of retries of a given operation. This counter is then mapped through one of the provided or user-created backoff strategies to produce a duration of time for the time.Sleep function. The retry package is inspired by the stateless backoff techniques described in http://blog.gopheracademy.com/advent-2014/backoff/

Complex backoff strategies can be built by using the methods defined in the retry package to add random splay, overwrite, or otherwise manipulate the values returned by a Strategy.

Code:

// mock for an unreliable remote service
getdump := func(i int) error {
    if i%3 == 0 {
        return nil
    }
    return errors.New("remote call failed")
}
// Request a dump from a service every hour. If something goes
// wrong, retry on lengthening intervals until we get a response,
// then go back to per-hour dumps.
backoff := retry.Exponential(time.Minute).Shift(2).
    Unshift(time.Hour)
try := 0

for i := 0; i < 7; i++ {
    if err := getdump(i); err != nil {
        try++
        fmt.Println(err)
    } else {
        try = 0
        fmt.Println("success")
    }
    fmt.Printf("sleeping %s\n", backoff(try))
}

Output:

success
sleeping 1h0m0s
remote call failed
sleeping 4m0s
remote call failed
sleeping 8m0s
success
sleeping 1h0m0s
remote call failed
sleeping 4m0s
remote call failed
sleeping 8m0s
success
sleeping 1h0m0s

Index

Examples

Package Files

retry.go

type Strategy Uses

type Strategy func(nth int) time.Duration

A Strategy is a mapping from a retry counter to a duration of time. The retry package provides a number of built-in Strategies that capture the most common use cases.

func Exponential Uses

func Exponential(units time.Duration) Strategy

Exponential creates an exponential backoff Strategy that returns 2ⁿ units. The values returned by Exponential will increase up to the maximum value of time.Duration and will not overflow.

Code:

backoff := retry.Exponential(time.Second)

for try := 0; try < 10; try++ {
    fmt.Printf("Connection failed; will try again in %s\n", backoff(try))
}

Output:

Connection failed; will try again in 1s
Connection failed; will try again in 2s
Connection failed; will try again in 4s
Connection failed; will try again in 8s
Connection failed; will try again in 16s
Connection failed; will try again in 32s
Connection failed; will try again in 1m4s
Connection failed; will try again in 2m8s
Connection failed; will try again in 4m16s
Connection failed; will try again in 8m32s

func Intervals Uses

func Intervals(dur ...time.Duration) Strategy

Intervals creates a backoff policy that selects the nth duration in the argument list. If the retry counter is greater than the number of items provided, the final item is returned. If the retry counter is less than 0 the first item is returned. If the parameter list is empty, the returned strategy will always return 0.

Code:

backoff := retry.Intervals(time.Minute, time.Hour, time.Hour*2)

for try := 0; try < 4; try++ {
    fmt.Println(backoff(try))
}

Output:

1m0s
1h0m0s
2h0m0s
2h0m0s

Code:

backoff := retry.Intervals(1, 2, 3, 4, 5, 6, 7).Scale(time.Microsecond)

for try := 0; try < 7; try++ {
    fmt.Println(backoff(try))
}

Output:

1µs
2µs
3µs
4µs
5µs
6µs
7µs

func Milliseconds Uses

func Milliseconds(ms ...int) Strategy

Milliseconds creates a backoff policy that selects the nth item in the array, multiplied by time.Millisecond. If the retry counter is greater than the number of items provided, the final item is returned.

Code:

backoff := retry.Milliseconds(2, 4, 6, 22, 39, 18)

for try := 0; try < 8; try++ {
    fmt.Printf("Connection failed; will try again in %s\n", backoff(try))
}

Output:

Connection failed; will try again in 2ms
Connection failed; will try again in 4ms
Connection failed; will try again in 6ms
Connection failed; will try again in 22ms
Connection failed; will try again in 39ms
Connection failed; will try again in 18ms
Connection failed; will try again in 18ms
Connection failed; will try again in 18ms

func Seconds Uses

func Seconds(secs ...int) Strategy

Seconds creates a backoff policy that selects the nth item in the array, multiplied by time.Second.

Code:

backoff := retry.Seconds(2, 4, 6, 22, 39, 18)

for try := 0; try < 10; try++ {
    fmt.Printf("Connection failed; will try again in %s\n", backoff(try))
}

Output:

Connection failed; will try again in 2s
Connection failed; will try again in 4s
Connection failed; will try again in 6s
Connection failed; will try again in 22s
Connection failed; will try again in 39s
Connection failed; will try again in 18s
Connection failed; will try again in 18s
Connection failed; will try again in 18s
Connection failed; will try again in 18s
Connection failed; will try again in 18s

func (Strategy) Max Uses

func (base Strategy) Max(max time.Duration) Strategy

The Max method imposes a maximum value on the durations returned by a Strategy. Values returned by the resulting Strategy will always be less than or equal to max

func (Strategy) Min Uses

func (base Strategy) Min(min time.Duration) Strategy

The Min method imposes a minimum value on the durations returned by a Strategy. Values returned by the resulting Strategy will always be greater than or equal to min.

func (Strategy) Overwrite Uses

func (base Strategy) Overwrite(dur ...time.Duration) Strategy

Overwrite replaces the first len(dur) mappings of a Strategy, selecting durations from the given parameter list instead. Passing len(dur) to the returned strategy is equivalent to passing len(dur) to the original strategy.

func (Strategy) Scale Uses

func (base Strategy) Scale(units time.Duration) Strategy

Scale multiplies all values returned by a fixed duration.

Code:

// Sleep for 2ⁿ milliseconds, not nanoseconds
backoff := retry.Exponential(1).Scale(time.Millisecond)

for try := 0; try < 10; try++ {
    fmt.Println(backoff(try))
}

Output:

1ms
2ms
4ms
8ms
16ms
32ms
64ms
128ms
256ms
512ms

func (Strategy) Shift Uses

func (base Strategy) Shift(n int) Strategy

Shift skips the first n values of a Strategy. Passing 0+i to the returned Strategy is equivalent to passing n+i to the original Strategy.

Code:

backoff := retry.Seconds(1, 2, 3, 4, 5, 6, 7).Shift(2)

for try := 0; try < 10; try++ {
    fmt.Println(backoff(try))
}

Output:

3s
4s
5s
6s
7s
7s
7s
7s
7s
7s

func (Strategy) Splay Uses

func (base Strategy) Splay(duration time.Duration) Strategy

Splay adds a random duration in the range ±duration to values returned by a Strategy. Splay is useful for avoiding "thundering herd" scenarios, where multiple processes become inadvertently synchronized and use the same backoff strategy to use a shared service.

Code:

backoff := retry.Exponential(time.Second).Splay(time.Second / 2)

for try := 0; try < 10; try++ {
    fmt.Println(backoff(try))
}

func (Strategy) Unshift Uses

func (base Strategy) Unshift(dur ...time.Duration) Strategy

Unshift displaces the first len(dur) mappings of a Strategy, selecting durations from the given parameter list instead. Passing len(dur) to the returned strategy is equivalent to passing 0 to the original strategy.

Code:

backoff := retry.Exponential(time.Minute).
    Unshift(time.Hour)

for try := 0; try < 10; try++ {
    fmt.Printf("%d: %s\n", try, backoff(try))
}

Output:

0: 1h0m0s
1: 1m0s
2: 2m0s
3: 4m0s
4: 8m0s
5: 16m0s
6: 32m0s
7: 1h4m0s
8: 2h8m0s
9: 4h16m0s

Package retry imports 6 packages (graph) and is imported by 2 packages. Updated 2017-05-27. Refresh now. Tools for package owners.