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
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.
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
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
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
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
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
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.
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.
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
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
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)) }
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 4 packages. Updated 2018-05-04. Refresh now. Tools for package owners.