]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package client |
2 | ||
3 | import ( | |
4 | "math/rand" | |
5 | "sync" | |
6 | "time" | |
7 | ||
8 | "github.com/aws/aws-sdk-go/aws/request" | |
9 | ) | |
10 | ||
11 | // DefaultRetryer implements basic retry logic using exponential backoff for | |
12 | // most services. If you want to implement custom retry logic, implement the | |
13 | // request.Retryer interface or create a structure type that composes this | |
14 | // struct and override the specific methods. For example, to override only | |
15 | // the MaxRetries method: | |
16 | // | |
17 | // type retryer struct { | |
18 | // service.DefaultRetryer | |
19 | // } | |
20 | // | |
21 | // // This implementation always has 100 max retries | |
22 | // func (d retryer) MaxRetries() uint { return 100 } | |
23 | type DefaultRetryer struct { | |
24 | NumMaxRetries int | |
25 | } | |
26 | ||
27 | // MaxRetries returns the number of maximum returns the service will use to make | |
28 | // an individual API request. | |
29 | func (d DefaultRetryer) MaxRetries() int { | |
30 | return d.NumMaxRetries | |
31 | } | |
32 | ||
33 | var seededRand = rand.New(&lockedSource{src: rand.NewSource(time.Now().UnixNano())}) | |
34 | ||
35 | // RetryRules returns the delay duration before retrying this request again | |
36 | func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration { | |
37 | // Set the upper limit of delay in retrying at ~five minutes | |
38 | minTime := 30 | |
39 | throttle := d.shouldThrottle(r) | |
40 | if throttle { | |
41 | minTime = 500 | |
42 | } | |
43 | ||
44 | retryCount := r.RetryCount | |
45 | if retryCount > 13 { | |
46 | retryCount = 13 | |
47 | } else if throttle && retryCount > 8 { | |
48 | retryCount = 8 | |
49 | } | |
50 | ||
51 | delay := (1 << uint(retryCount)) * (seededRand.Intn(minTime) + minTime) | |
52 | return time.Duration(delay) * time.Millisecond | |
53 | } | |
54 | ||
55 | // ShouldRetry returns true if the request should be retried. | |
56 | func (d DefaultRetryer) ShouldRetry(r *request.Request) bool { | |
57 | // If one of the other handlers already set the retry state | |
58 | // we don't want to override it based on the service's state | |
59 | if r.Retryable != nil { | |
60 | return *r.Retryable | |
61 | } | |
62 | ||
63 | if r.HTTPResponse.StatusCode >= 500 { | |
64 | return true | |
65 | } | |
66 | return r.IsErrorRetryable() || d.shouldThrottle(r) | |
67 | } | |
68 | ||
69 | // ShouldThrottle returns true if the request should be throttled. | |
70 | func (d DefaultRetryer) shouldThrottle(r *request.Request) bool { | |
71 | if r.HTTPResponse.StatusCode == 502 || | |
72 | r.HTTPResponse.StatusCode == 503 || | |
73 | r.HTTPResponse.StatusCode == 504 { | |
74 | return true | |
75 | } | |
76 | return r.IsErrorThrottle() | |
77 | } | |
78 | ||
79 | // lockedSource is a thread-safe implementation of rand.Source | |
80 | type lockedSource struct { | |
81 | lk sync.Mutex | |
82 | src rand.Source | |
83 | } | |
84 | ||
85 | func (r *lockedSource) Int63() (n int64) { | |
86 | r.lk.Lock() | |
87 | n = r.src.Int63() | |
88 | r.lk.Unlock() | |
89 | return | |
90 | } | |
91 | ||
92 | func (r *lockedSource) Seed(seed int64) { | |
93 | r.lk.Lock() | |
94 | r.src.Seed(seed) | |
95 | r.lk.Unlock() | |
96 | } |