Simpler backoff

30 points by carlana


hobbified

We have vimeo/go-retry which

A) lets you collapse this example down to

err := retry.Retry(ctx, retry.DefaultBackoff(), 10, request)
if err != nil { /* ... */ }

(convenient that the request in this example is a func(context.Context) error, but of course if it wasn’t you could wrap it in an anonymous function).

B) Has a generics-using version that makes it easy to wrap functions that return some value as well as an error. If request was a func(context.Context) (Response, error) you could

res, err := retry.Typed(ctx, retry.NewRetryable(10), request)

and res would be a Response, no unwrapping needed.

C) Has a hook to let you class errors as retryable or permanent.

D) Has the Backoff as its own type so that if you want to do something we didn’t imagine you still have the option of writing something like

backoff := retry.Backoff{
    MinBackoff: time.Second,
    MaxBackoff: 60*time.Second,
    Jitter: 0.25,
    ExpFactor: 1.25,
}

for {
    // ...
    time.Sleep(backoff.Next())
}
adriano

I prefer jitter when I have a larger number of uncoordinated systems likely to be retrying a thing for the same reason (e.g. overloaded backend system)

I prefer exponential backoff when I have a large number of uncoordinated system likely to overload long-running things by retrying.

If a system has neither of these properties, exponential backoff is probably overkill. But it’s also not terribly complex, so the downsides of using it when it’s overkill are not significant.

olegkovalov

Nice, indeed, sometimes a fixed set of values is simpler then a general solution. Also YMMV :)

But I had another case when error can be retried or not. My old solution for this https://github.com/cristalhq/synx/blob/main/backoff.go#L17

mariusor

For a separate library I created some retry with back-off functionality which in my opinion encodes the intention of the developer in a clearer, and more configurable way, than the lookup table - which I really like for very simple cases.

My library uses something like:

Retry(5, BackOff(2*time.Seconds, Linear(1.4), func(_ context.Context) {/* do things return errors */} )