retry.go
25 lines · 0.5 KB
package retry
import (
"context"
"math/rand"
"time"
)
// Do calls fn until it succeeds, the attempts run out, or ctx is done.
func Do(ctx context.Context, attempts int, base time.Duration, fn func() error) error {
var err error
for i := 0; i < attempts; i++ {
if err = fn(); err == nil {
return nil
}
jitter := time.Duration(rand.Int63n(int64(base)))
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(base<<i + jitter):
}
}
return err
}
retry_test.go
20 lines · 0.3 KB
package retry_test
import (
"context"
"errors"
"testing"
"time"
)
func TestDoGivesUp(t *testing.T) {
calls := 0
err := Do(context.Background(), 3, time.Millisecond, func() error {
calls++
return errors.New("boom")
})
if err == nil || calls != 3 {
t.Fatalf("calls=%d err=%v", calls, err)
}
}