forked from viant/bigquery
/
retry.go
69 lines (62 loc) · 1.43 KB
/
retry.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
package bigquery
import (
"google.golang.org/api/googleapi"
"math/rand"
"net/http"
"time"
)
func shallRetry(err error) bool {
if apiError, ok := err.(*googleapi.Error); ok {
switch apiError.Code {
case http.StatusInternalServerError, http.StatusServiceUnavailable, http.StatusBadGateway:
return true
}
}
return false
}
//retrier represents abstraction holding sleep duration between retries (back-off)
type retrier struct {
Count int
Initial time.Duration
Max time.Duration
Multiplier float64
duration time.Duration
}
// Pause returns the next time.Duration that the caller should use to backoff.
func (b *retrier) Pause() time.Duration {
if b.Initial == 0 {
b.Initial = time.Second
}
if b.duration == 0 {
b.duration = b.Initial
}
if b.Max == 0 {
b.Max = 30 * time.Second
}
if b.Multiplier < 1 {
b.Multiplier = 2
}
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
result := time.Duration(1 + rnd.Int63n(int64(b.duration)))
b.duration = time.Duration(float64(b.duration) * b.Multiplier)
if b.duration > b.Max {
b.duration = b.Max
}
return result
}
//newRetries creates a retrier
func newRetries() *retrier {
return &retrier{}
}
//runWithRetries run with retries
func runWithRetries(f func() error, maxRetries int) (err error) {
aRetrier := newRetries()
for i := 0; i < maxRetries; i++ {
err = f()
if !shallRetry(err) {
return err
}
time.Sleep(aRetrier.Pause())
}
return err
}