retry.go (84 lines of code) (raw):

package sls import ( "golang.org/x/net/context" "github.com/cenkalti/backoff" "github.com/pkg/errors" ) // Retry execute the input operation immediately at first, // and do an exponential backoff retry when failed. // The default max elapsed time is 15 minutes. // The default retry intervals are shown below, in seconds. // // 1 0.5 [0.25, 0.75] // 2 0.75 [0.375, 1.125] // 3 1.125 [0.562, 1.687] // 4 1.687 [0.8435, 2.53] // 5 2.53 [1.265, 3.795] // 6 3.795 [1.897, 5.692] // 7 5.692 [2.846, 8.538] // 8 8.538 [4.269, 12.807] // 9 12.807 [6.403, 19.210] // // ... // The signature of backoff.Operation is "func() error". func Retry(ctx context.Context, o backoff.Operation) error { return RetryWithBackOff(ctx, backoff.NewExponentialBackOff(), o) } // RetryWithBackOff ... func RetryWithBackOff(ctx context.Context, b backoff.BackOff, o backoff.Operation) error { ticker := backoff.NewTicker(b) defer ticker.Stop() var err error for { select { case <-ctx.Done(): return errors.Wrapf(ctx.Err(), "stopped retrying err: %v", err) default: select { case _, ok := <-ticker.C: if !ok { return err } err = o() if err == nil { return nil } case <-ctx.Done(): return errors.Wrapf(ctx.Err(), "stopped retrying err: %v", err) } } } } // ConditionOperation : retry depends on the retured bool type ConditionOperation func() (bool, error) // RetryWithCondition ... func RetryWithCondition(ctx context.Context, b backoff.BackOff, o ConditionOperation) error { ticker := backoff.NewTicker(b) defer ticker.Stop() var err error var needRetry bool for { select { case <-ctx.Done(): return errors.Wrapf(ctx.Err(), "stopped retrying err: %v", err) default: select { case _, ok := <-ticker.C: if !ok { return err } needRetry, err = o() if !needRetry { return err } case <-ctx.Done(): return errors.Wrapf(ctx.Err(), "stopped retrying err: %v", err) } } } } // RetryWithAttempt ... func RetryWithAttempt(ctx context.Context, maxAttempt int, o ConditionOperation) error { b := backoff.NewExponentialBackOff() ticker := backoff.NewTicker(b) defer ticker.Stop() var err error var needRetry bool for try := 0; try < maxAttempt; try++ { // make sure we check ctx.Done() first select { case <-ctx.Done(): return errors.Wrapf(ctx.Err(), "stopped retrying err: %v", err) default: } select { case _, ok := <-ticker.C: if !ok { return err } needRetry, err = o() if !needRetry { return err } } } return err }