Class: Excon::Middleware::AWS::ExponentialBackoff

Inherits:
Base
  • Object
show all
Defined in:
lib/excon/middleware/aws/exponential_backoff.rb,
lib/excon/middleware/aws/exponential_backoff/version.rb

Constant Summary collapse

MILLISECOND =
1.0/1000
SLEEP_FACTOR =
MILLISECOND * 100
ERROR_CODE_REGEX =
Regexp.new(/<Code>([^<]+)<\/Code>/mi)
THROTTLING_ERROR_CODES =
%w[
                           Throttling
                           ThrottlingException
                           ProvisionedThroughputExceededException
                           RequestThrottled
                           RequestLimitExceeded
                           BandwidthLimitExceeded
]
SERVER_ERROR_CLASSES =
[
 Excon::Errors::InternalServerError,
 Excon::Errors::BadGateway,
 Excon::Errors::ServiceUnavailable,
 Excon::Errors::GatewayTimeout
]
VALID_MIDDLEWARE_KEYS =
[
 :backoff
]
VERSION =
"0.0.2"

Instance Method Summary collapse

Instance Method Details

#do_backoff(datum) ⇒ Object



46
47
48
49
50
51
52
# File 'lib/excon/middleware/aws/exponential_backoff.rb', line 46

def do_backoff(datum)
  do_sleep(sleep_time(datum), datum)
  datum[:backoff][:retry_count] += 1
  connection = datum.delete(:connection)
  datum.reject! { |key, _| !(Excon::VALID_REQUEST_KEYS + VALID_MIDDLEWARE_KEYS).include?(key)  }
  connection.request(datum)
end

#do_handoff(datum) ⇒ Object



42
43
44
# File 'lib/excon/middleware/aws/exponential_backoff.rb', line 42

def do_handoff(datum)
  @stack.error_call(datum)
end

#do_sleep(sleep_time, datum) ⇒ Object



54
55
56
57
58
59
60
61
62
# File 'lib/excon/middleware/aws/exponential_backoff.rb', line 54

def do_sleep(sleep_time, datum)
  if datum.has_key?(:instrumentor)
    datum[:instrumentor].instrument("#{datum[:instrumentor_name]}.backoff", datum) do
      sleep sleep_time
    end
  else
    sleep sleep_time
  end
end

#error_call(datum) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/excon/middleware/aws/exponential_backoff.rb', line 29

def error_call(datum)
  datum[:backoff] ||= {}
  datum[:backoff][:max_retries] ||= 0
  datum[:backoff][:max_delay]   ||= 30
  datum[:backoff][:retry_count] ||= 0

  if (throttle?(datum) || server_error?(datum)) && should_retry?(datum)
    do_backoff(datum)
  else
    do_handoff(datum)
  end
end

#extract_error_code(body) ⇒ Object



89
90
91
92
93
94
# File 'lib/excon/middleware/aws/exponential_backoff.rb', line 89

def extract_error_code(body)
  match = ERROR_CODE_REGEX.match(body)
  if match && code = match[1]
    code.strip if code
  end
end

#server_error?(datum) ⇒ Boolean

Returns:

  • (Boolean)


85
86
87
# File 'lib/excon/middleware/aws/exponential_backoff.rb', line 85

def server_error?(datum)
  SERVER_ERROR_CLASSES.any? { |ex| datum[:error].kind_of?(ex) }
end

#should_retry?(datum) ⇒ Boolean

Returns:

  • (Boolean)


72
73
74
75
76
# File 'lib/excon/middleware/aws/exponential_backoff.rb', line 72

def should_retry?(datum)
  # Always retry if max_retries is 0.
  datum[:backoff][:max_retries] == 0 ||
    datum[:backoff][:retry_count] < datum[:backoff][:max_retries]
end

#sleep_time(datum) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/excon/middleware/aws/exponential_backoff.rb', line 64

def sleep_time(datum)
  exponential_wait = (2 ** datum[:backoff][:retry_count] + rand(0.0)) * SLEEP_FACTOR
  [
   exponential_wait, 
   datum[:backoff][:max_delay]
  ].min.round(2)
end

#throttle?(datum) ⇒ Boolean

Returns:

  • (Boolean)


78
79
80
81
82
83
# File 'lib/excon/middleware/aws/exponential_backoff.rb', line 78

def throttle?(datum)
  datum[:error].kind_of?(Excon::Errors::BadRequest) &&
    THROTTLING_ERROR_CODES.include?(extract_error_code(datum[:error].response.body))


end