blob: 5dbd825b5c8b5761dcb613d198ea9662b761b1a7 [file] [log] [blame]
Serge Bazanskicc25bdf2018-10-25 14:02:58 +02001package backoff
2
3import "time"
4
5// An Operation is executing by Retry() or RetryNotify().
6// The operation will be retried using a backoff policy if it returns an error.
7type Operation func() error
8
9// Notify is a notify-on-error function. It receives an operation error and
10// backoff delay if the operation failed (with an error).
11//
12// NOTE that if the backoff policy stated to stop retrying,
13// the notify function isn't called.
14type Notify func(error, time.Duration)
15
16// Retry the operation o until it does not return error or BackOff stops.
17// o is guaranteed to be run at least once.
18// It is the caller's responsibility to reset b after Retry returns.
19//
20// If o returns a *PermanentError, the operation is not retried, and the
21// wrapped error is returned.
22//
23// Retry sleeps the goroutine for the duration returned by BackOff after a
24// failed operation returns.
25func Retry(o Operation, b BackOff) error { return RetryNotify(o, b, nil) }
26
27// RetryNotify calls notify function with the error and wait duration
28// for each failed attempt before sleep.
29func RetryNotify(operation Operation, b BackOff, notify Notify) error {
30 var err error
31 var next time.Duration
32
33 cb := ensureContext(b)
34
35 b.Reset()
36 for {
37 if err = operation(); err == nil {
38 return nil
39 }
40
41 if permanent, ok := err.(*PermanentError); ok {
42 return permanent.Err
43 }
44
45 if next = b.NextBackOff(); next == Stop {
46 return err
47 }
48
49 if notify != nil {
50 notify(err, next)
51 }
52
53 t := time.NewTimer(next)
54
55 select {
56 case <-cb.Context().Done():
57 t.Stop()
58 return err
59 case <-t.C:
60 }
61 }
62}
63
64// PermanentError signals that the operation should not be retried.
65type PermanentError struct {
66 Err error
67}
68
69func (e *PermanentError) Error() string {
70 return e.Err.Error()
71}
72
73// Permanent wraps the given err in a *PermanentError.
74func Permanent(err error) *PermanentError {
75 return &PermanentError{
76 Err: err,
77 }
78}