-
Notifications
You must be signed in to change notification settings - Fork 0
/
r.go
74 lines (61 loc) · 1.92 KB
/
r.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package r
import (
"errors"
"time"
"github.com/yawboakye/r/backoff"
)
// A manifest can be used just once. In case an attempt
// is made to use it again, this error is returned. If
// you desire to trial the manifest again, you should make
// a new one instead.
var errUsed = errors.New("r: manifest already used. cannot be used more than once")
// F is a manifest for a retry-able function/method.
// It defines what function to retry, maximum retries
// before returning the ensuing error, and the backoff
// strategy to use between retries.
type F struct {
Fn func(...interface{}) (interface{}, error)
MaxRetries int
Backoff backoff.Strategy
tries int
used bool
}
// Tried returns the number of times the function was tried.
// This is only informational if the function succeeded
// after a number of calls. In that case it could be
// different and lower than `MaxRetries`.
func (f *F) Tried() int { return f.tries }
// Have we run out of retries?
func (f *F) exhausted() bool { return f.tries == f.MaxRetries }
// backoff waits for a period between two retries
// of a function. How long it waits for depends
// on the backoff strategy.
func (f *F) backoff() {
time.Sleep(f.Backoff.WaitDur(f.tries))
}
// Run runs the function, retrying on failure until the
// maximum number of retries is exceeded.
func (f *F) Run(args ...interface{}) (res interface{}, err error) {
// Every manifest can be used just once. After
// it has been used it becomes invalid. This ensure
// idempotency, if the function succeed during one
// of the trials.
if f.used {
return nil, errUsed
}
// Consider the manifest used even before the first attempt to
// invoke the function.
f.used = true
for {
f.tries++
res, err = f.Fn(args...)
if err == nil || f.exhausted() {
break
}
// The bad thing happened.
// Wait for the duration decided by the backoff
// strategy, and then try again.
f.backoff()
}
return
}