Skip to content

Commit dee5c6b

Browse files
author
Matt Muller
committed
Refactor retry exponential backoff
1 parent bf4919c commit dee5c6b

7 files changed

Lines changed: 85 additions & 39 deletions

File tree

gems/smithy-client/lib/smithy-client/plugins/retry_errors.rb

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,37 @@ class RetryErrors < Plugin
3333
DOCS
3434

3535
option(
36-
:retry_backoff,
37-
default: Retry::EXPONENTIAL_BACKOFF,
38-
doc_default: 'Smithy::Client::Retry::EXPONENTIAL_BACKOFF',
39-
doc_type: 'lambda',
36+
:retry_max_delay,
37+
default: 20,
38+
docstring: <<~DOCS)
39+
The maximum delay, in seconds, between retry attempts. This option is ignored
40+
if a custom `retry_backoff` is provided. Used in the `standard` and `adaptive`
41+
retry strategies.
42+
DOCS
43+
44+
option(
45+
:retry_base_delay,
46+
default: 2,
4047
docstring: <<~DOCS)
48+
The base delay, in seconds, used to calculate the exponential backoff for
49+
retry attempts. This option is ignored if a custom `retry_backoff` is provided.
50+
Used in the `standard` and `adaptive` retry strategies.
51+
DOCS
52+
53+
option(
54+
:retry_backoff,
55+
doc_default: 'Smithy::Client::Retry::ExponentialBackoff.new',
56+
doc_type: '#call(attempts)',
57+
docstring: <<~DOCS) do |config|
4158
A callable object that calculates a backoff delay for a retry attempt. The callable
4259
should accept a single argument, `attempts`, that represents the number of attempts
4360
that have been made. Used in the `standard` and `adaptive` retry strategies.
4461
DOCS
62+
Retry::ExponentialBackoff.new(
63+
retry_base_delay: config.retry_base_delay,
64+
retry_max_delay: config.retry_max_delay
65+
)
66+
end
4567

4668
option(
4769
:adaptive_retry_wait_to_fill,

gems/smithy-client/lib/smithy-client/retry.rb

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,7 @@
22

33
require_relative 'retry/adaptive'
44
require_relative 'retry/client_rate_limiter'
5+
require_relative 'retry/exponential_backoff'
56
require_relative 'retry/quota'
67
require_relative 'retry/standard'
7-
8-
module Smithy
9-
module Client
10-
module Retry
11-
# The maximum backoff delay for retrying requests.
12-
MAX_BACKOFF = 20
13-
14-
# The default backoff for retrying requests.
15-
EXPONENTIAL_BACKOFF = lambda do |attempts|
16-
[Kernel.rand * (2**attempts), MAX_BACKOFF].min || 0
17-
end
18-
19-
# Represents a token that can be used to retry an operation.
20-
class Token
21-
def initialize
22-
@retry_count = 0
23-
@retry_delay = 0
24-
end
25-
26-
# The number of times the operation has been retried.
27-
# @return [Integer]
28-
attr_accessor :retry_count
29-
30-
# The delay before the next retry.
31-
# @return [Numeric]
32-
attr_accessor :retry_delay
33-
end
34-
end
35-
end
36-
end
8+
require_relative 'retry/token'

gems/smithy-client/lib/smithy-client/retry/adaptive.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module Client
55
module Retry
66
# Adaptive retry strategy for retrying requests.
77
class Adaptive
8-
# @option [#call] :backoff (EXPONENTIAL_BACKOFF) A callable object that
8+
# @option [#call] :backoff (ExponentialBackoff.new) A callable object that
99
# calculates a backoff delay for a retry attempt.
1010
# @option [Integer] :max_attempts (3) The maximum number of attempts that
1111
# will be made for a single request, including the initial attempt.
@@ -15,7 +15,10 @@ class Adaptive
1515
# not retry instead of sleeping.
1616
def initialize(options = {})
1717
super()
18-
@backoff = options[:backoff] || EXPONENTIAL_BACKOFF
18+
@backoff = options[:backoff] || ExponentialBackoff.new(
19+
base_delay: options[:base_delay],
20+
max_delay: options[:max_delay]
21+
)
1922
@max_attempts = options[:max_attempts] || 3
2023
@wait_to_fill = options[:wait_to_fill] || true
2124
@client_rate_limiter = ClientRateLimiter.new
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# frozen_string_literal: true
2+
3+
module Smithy
4+
module Client
5+
module Retry
6+
# Default exponential backoff retry strategy for retrying requests.
7+
class ExponentialBackoff
8+
def initialize(options = {})
9+
@base_delay = options[:base_delay] || 2
10+
@max_delay = options[:max_delay] || 20
11+
end
12+
13+
# Calculates a delay based on exponential backoff strategy. Uses full jitter approach.
14+
# @param [Integer] attempts
15+
# @return [Float] delay in seconds
16+
def call(attempts)
17+
delay = (@base_delay**attempts)
18+
[delay, @max_delay].min * Kernel.rand
19+
end
20+
end
21+
end
22+
end
23+
end

gems/smithy-client/lib/smithy-client/retry/standard.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ module Client
55
module Retry
66
# Standard retry strategy for retrying requests.
77
class Standard
8-
# @option [#call] :backoff (EXPONENTIAL_BACKOFF) A callable object that
8+
# @option [#call] :backoff (ExponentialBackoff.new) A callable object that
99
# calculates a backoff delay for a retry attempt.
1010
# @option [Integer] :max_attempts (3) The maximum number of attempts that
1111
# will be made for a single request, including the initial attempt.
1212
def initialize(options = {})
1313
super()
14-
@backoff = options[:backoff] || EXPONENTIAL_BACKOFF
14+
@backoff = options[:backoff] || ExponentialBackoff.new(
15+
base_delay: options[:base_delay],
16+
max_delay: options[:max_delay]
17+
)
1518
@max_attempts = options[:max_attempts] || 3
1619
@quota = Quota.new
1720
@capacity_amount = 0
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# frozen_string_literal: true
2+
3+
module Smithy
4+
module Client
5+
module Retry
6+
# Represents a token that can be used to retry an operation.
7+
class Token
8+
def initialize
9+
@retry_count = 0
10+
@retry_delay = 0
11+
end
12+
13+
# The number of times the operation has been retried.
14+
# @return [Integer]
15+
attr_accessor :retry_count
16+
17+
# The delay before the next retry.
18+
# @return [Numeric]
19+
attr_accessor :retry_delay
20+
end
21+
end
22+
end
23+
end

gems/smithy-client/spec/smithy-client/plugins/retry_errors_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,8 @@ module Plugins
203203
end
204204

205205
it 'does not exceed the max backoff time' do
206+
config.retry_strategy = Retry::Standard.new(max_delay: 3)
206207
retry_strategy.instance_variable_set(:@max_attempts, 5)
207-
stub_const('Smithy::Client::Retry::MAX_BACKOFF', 3)
208208

209209
test_case_def = [
210210
{

0 commit comments

Comments
 (0)