README.md

  1# Retry
  2
  3[![GoDoc](https://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://pkg.go.dev/mod/github.com/sethvargo/go-retry)
  4
  5Retry is a Go library for facilitating retry logic and backoff. It's highly
  6extensible with full control over how and when retries occur. You can also write
  7your own custom backoff functions by implementing the Backoff interface.
  8
  9## Features
 10
 11- **Extensible** - Inspired by Go's built-in HTTP package, this Go backoff and
 12  retry library is extensible via middleware. You can write custom backoff
 13  functions or use a provided filter.
 14
 15- **Independent** - No external dependencies besides the Go standard library,
 16  meaning it won't bloat your project.
 17
 18- **Concurrent** - Unless otherwise specified, everything is safe for concurrent
 19  use.
 20
 21- **Context-aware** - Use native Go contexts to control cancellation.
 22
 23## Usage
 24
 25Here is an example use for connecting to a database using Go's `database/sql`
 26package:
 27
 28```golang
 29package main
 30
 31import (
 32  "context"
 33  "database/sql"
 34  "log"
 35  "time"
 36
 37  "github.com/sethvargo/go-retry"
 38)
 39
 40func main() {
 41  db, err := sql.Open("mysql", "...")
 42  if err != nil {
 43    log.Fatal(err)
 44  }
 45
 46  ctx := context.Background()
 47  if err := retry.Fibonacci(ctx, 1*time.Second, func(ctx context.Context) error {
 48    if err := db.PingContext(ctx); err != nil {
 49      // This marks the error as retryable
 50      return retry.RetryableError(err)
 51    }
 52    return nil
 53  }); err != nil {
 54    log.Fatal(err)
 55  }
 56}
 57```
 58
 59## Backoffs
 60
 61In addition to your own custom algorithms, there are built-in algorithms for
 62backoff in the library.
 63
 64### Constant
 65
 66A very rudimentary backoff, just returns a constant value. Here is an example:
 67
 68```text
 691s -> 1s -> 1s -> 1s -> 1s -> 1s
 70```
 71
 72Usage:
 73
 74```golang
 75NewConstant(1 * time.Second)
 76```
 77
 78### Exponential
 79
 80Arguably the most common backoff, the next value is double the previous value.
 81Here is an example:
 82
 83```text
 841s -> 2s -> 4s -> 8s -> 16s -> 32s -> 64s
 85```
 86
 87Usage:
 88
 89```golang
 90NewExponential(1 * time.Second)
 91```
 92
 93### Fibonacci
 94
 95The Fibonacci backoff uses the Fibonacci sequence to calculate the backoff. The
 96next value is the sum of the current value and the previous value. This means
 97retires happen quickly at first, but then gradually take slower, ideal for
 98network-type issues. Here is an example:
 99
100```text
1011s -> 1s -> 2s -> 3s -> 5s -> 8s -> 13s
102```
103
104Usage:
105
106```golang
107NewFibonacci(1 * time.Second)
108```
109
110## Modifiers (Middleware)
111
112The built-in backoff algorithms never terminate and have no caps or limits - you
113control their behavior with middleware. There's built-in middleware, but you can
114also write custom middleware.
115
116### Jitter
117
118To reduce the changes of a thundering herd, add random jitter to the returned
119value.
120
121```golang
122b := NewFibonacci(1 * time.Second)
123
124// Return the next value, +/- 500ms
125b = WithJitter(500*time.Millisecond, b)
126
127// Return the next value, +/- 5% of the result
128b = WithJitterPercent(5, b)
129```
130
131### MaxRetries
132
133To terminate a retry, specify the maximum number of _retries_. Note this
134is _retries_, not _attempts_. Attempts is retries + 1.
135
136```golang
137b := NewFibonacci(1 * time.Second)
138
139// Stop after 4 retries, when the 5th attempt has failed. In this example, the worst case elapsed
140// time would be 1s + 1s + 2s + 3s = 7s.
141b = WithMaxRetries(4, b)
142```
143
144### CappedDuration
145
146To ensure an individual calculated duration never exceeds a value, use a cap:
147
148```golang
149b := NewFibonacci(1 * time.Second)
150
151// Ensure the maximum value is 2s. In this example, the sleep values would be
152// 1s, 1s, 2s, 2s, 2s, 2s...
153b = WithCappedDuration(2 * time.Second, b)
154```
155
156### WithMaxDuration
157
158For a best-effort limit on the total execution time, specify a max duration:
159
160```golang
161b := NewFibonacci(1 * time.Second)
162
163// Ensure the maximum total retry time is 5s.
164b = WithMaxDuration(5 * time.Second, b)
165```
166
167## Benchmarks
168
169Here are benchmarks against some other popular Go backoff and retry libraries.
170You can run these benchmarks yourself via the `benchmark/` folder. Commas and
171spacing fixed for clarity.
172
173```text
174Benchmark/cenkalti-7      13,052,668     87.3 ns/op
175Benchmark/lestrrat-7         902,044    1,355 ns/op
176Benchmark/sethvargo-7    203,914,245     5.73 ns/op
177```
178
179## Notes and Caveats
180
181- Randomization uses `math/rand` seeded with the Unix timestamp instead of
182  `crypto/rand`.
183- Ordering of addition of multiple modifiers will make a difference.
184  For example; ensure you add `CappedDuration` before `WithMaxDuration`, otherwise it may early out too early.
185  Another example is you could add `Jitter` before or after capping depending on your desired outcome.