retry.go raw

   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