1 /*
2 * Copyright 2017 Baidu, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 * either express or implied. See the License for the specific language governing permissions
12 * and limitations under the License.
13 */
14 15 // retry.go - define the retry policy when making requests to BCE services
16 17 package bce
18 19 import (
20 "net"
21 "net/http"
22 "strings"
23 "time"
24 25 "github.com/baidubce/bce-sdk-go/util/log"
26 )
27 28 // RetryPolicy defines the two methods to retry for sending request.
29 type RetryPolicy interface {
30 ShouldRetry(BceError, int) bool
31 GetDelayBeforeNextRetryInMillis(BceError, int) time.Duration
32 }
33 34 // NoRetryPolicy just does not retry.
35 type NoRetryPolicy struct{}
36 37 func (_ *NoRetryPolicy) ShouldRetry(err BceError, attempts int) bool {
38 return false
39 }
40 41 func (_ *NoRetryPolicy) GetDelayBeforeNextRetryInMillis(
42 err BceError, attempts int) time.Duration {
43 return 0 * time.Millisecond
44 }
45 46 func NewNoRetryPolicy() *NoRetryPolicy {
47 return &NoRetryPolicy{}
48 }
49 50 // BackOffRetryPolicy implements a policy that retries with exponential back-off strategy.
51 // This policy will keep retrying until the maximum number of retries is reached. The delay time
52 // will be a fixed interval for the first time then 2 * interval for the second, 4 * internal for
53 // the third, and so on.
54 // In general, the delay time will be 2^number_of_retries_attempted*interval. When a maximum of
55 // delay time is specified, the delay time will never exceed this limit.
56 type BackOffRetryPolicy struct {
57 maxErrorRetry int
58 maxDelayInMillis int64
59 baseIntervalInMillis int64
60 }
61 62 func (b *BackOffRetryPolicy) ShouldRetry(err BceError, attempts int) bool {
63 // Do not retry any more when retry the max times
64 if attempts >= b.maxErrorRetry {
65 return false
66 }
67 68 if err == nil {
69 return true
70 }
71 72 // Always retry on IO error
73 if _, ok := err.(net.Error); ok {
74 // context canceled should not retry
75 if strings.Contains(err.Error(), "context deadline exceeded") ||
76 strings.Contains(err.Error(), "context canceled") {
77 return false
78 }
79 return true
80 }
81 82 // Only retry on a service error
83 if realErr, ok := err.(*BceServiceError); ok {
84 switch realErr.StatusCode {
85 case http.StatusInternalServerError:
86 log.Warn("retry for internal server error(500)")
87 return true
88 case http.StatusBadGateway:
89 log.Warn("retry for bad gateway(502)")
90 return true
91 case http.StatusServiceUnavailable:
92 log.Warn("retry for service unavailable(503)")
93 return true
94 case http.StatusBadRequest:
95 if realErr.Code != "Http400" {
96 return false
97 }
98 log.Warn("retry for bad request(400)")
99 return true
100 }
101 102 if realErr.Code == EREQUEST_EXPIRED {
103 log.Warn("retry for request expired")
104 return true
105 }
106 }
107 return false
108 }
109 110 func (b *BackOffRetryPolicy) GetDelayBeforeNextRetryInMillis(
111 err BceError, attempts int) time.Duration {
112 if attempts < 0 {
113 return 0 * time.Millisecond
114 }
115 delayInMillis := (1 << uint64(attempts)) * b.baseIntervalInMillis
116 if delayInMillis > b.maxDelayInMillis {
117 return time.Duration(b.maxDelayInMillis) * time.Millisecond
118 }
119 return time.Duration(delayInMillis) * time.Millisecond
120 }
121 122 func NewBackOffRetryPolicy(maxRetry int, maxDelay, base int64) *BackOffRetryPolicy {
123 return &BackOffRetryPolicy{maxRetry, maxDelay, base}
124 }
125