From 98a61d01593f4443d7f6b36d577a5b55fe84e4af Mon Sep 17 00:00:00 2001 From: Matt Muller Date: Tue, 11 Nov 2025 12:49:12 -0500 Subject: [PATCH 1/7] Changes needed for DDB --- .../lib/smithy-client/http/response.rb | 4 +-- .../lib/smithy-client/log_param_formatter.rb | 27 ++++++++++++------- .../lib/smithy-client/net_http/handler.rb | 7 +---- .../lib/smithy-client/pageable_response.rb | 3 ++- .../lib/smithy-client/param_converter.rb | 2 +- .../lib/smithy-client/param_validator.rb | 2 +- .../plugins/pageable_response.rb | 1 + gems/smithy/lib/smithy/views/client/client.rb | 2 +- gems/smithy/lib/smithy/views/client/module.rb | 6 +++-- gems/smithy/lib/smithy/views/client/schema.rb | 2 +- 10 files changed, 30 insertions(+), 26 deletions(-) diff --git a/gems/smithy-client/lib/smithy-client/http/response.rb b/gems/smithy-client/lib/smithy-client/http/response.rb index 6758340fe..aca58c34b 100644 --- a/gems/smithy-client/lib/smithy-client/http/response.rb +++ b/gems/smithy-client/lib/smithy-client/http/response.rb @@ -98,9 +98,7 @@ def signal_done(options = {}) @done = true emit(:done) else - msg = 'options must be empty or must contain :status_code, :headers, ' \ - 'and :body' - raise ArgumentError, msg + raise ArgumentError, 'options must be empty or must contain :status_code, :headers, and :body' end end diff --git a/gems/smithy-client/lib/smithy-client/log_param_formatter.rb b/gems/smithy-client/lib/smithy-client/log_param_formatter.rb index 57cd3e3c3..96c03d09e 100644 --- a/gems/smithy-client/lib/smithy-client/log_param_formatter.rb +++ b/gems/smithy-client/lib/smithy-client/log_param_formatter.rb @@ -15,9 +15,9 @@ def initialize(options = {}) def summarize(value) case value - when Array then "[#{array(value)}]" + when Array then array(value) when File then file(value) - when Hash then "{ #{hash(value)} }" + when Hash then hash(value) when Pathname then pathname(value) when String then string(value) when Tempfile then tempfile(value) @@ -36,17 +36,24 @@ def string(str) end def hash(hash) - hash.map do |key, value| - if key.is_a?(String) - "#{key.inspect} => #{summarize(value)}" - else - "#{key}: #{summarize(value)}" - end - end.join(', ') + return if hash.empty? + + res = + hash.map do |key, value| + if key.is_a?(String) + "#{key.inspect} => #{summarize(value)}" + else + "#{key}: #{summarize(value)}" + end + end.join(', ') + "{#{res}}" end def array(array) - array.map { |v| summarize(v) }.join(', ') + return if array.empty? + + res = array.map { |v| summarize(v) }.join(', ') + "[#{res}]" end def file(file) diff --git a/gems/smithy-client/lib/smithy-client/net_http/handler.rb b/gems/smithy-client/lib/smithy-client/net_http/handler.rb index 84748b960..180f5628a 100644 --- a/gems/smithy-client/lib/smithy-client/net_http/handler.rb +++ b/gems/smithy-client/lib/smithy-client/net_http/handler.rb @@ -137,12 +137,7 @@ def net_http_request_class(request) # @param [Http::Request] request # @return [Hash] def net_headers_for(request) - # Net::HTTP adds a default header for accept-encoding (2.0.0+). - # Setting a default empty value defeats this. - # Removing this is necessary for most services to not break request - # signatures as well as dynamodb crc32 checks (these fail if the - # response is gzipped). - headers = { 'accept-encoding' => '' } + headers = {} request.headers.each_pair do |key, value| headers[key] = value end diff --git a/gems/smithy-client/lib/smithy-client/pageable_response.rb b/gems/smithy-client/lib/smithy-client/pageable_response.rb index 2f9fa1046..8ccdc0a3c 100644 --- a/gems/smithy-client/lib/smithy-client/pageable_response.rb +++ b/gems/smithy-client/lib/smithy-client/pageable_response.rb @@ -130,8 +130,9 @@ def next_page_params(params) prev_tokens = @paginator.prev_tokens(context.params) # Remove all previous tokens from original params # Sometimes a token can be nil and merge would not include it. - new_params = context.params.except(*prev_tokens) + new_params = context[:original_params].except(*prev_tokens) new_params.merge!(@paginator.next_tokens(data).merge(params)) + new_params end end end diff --git a/gems/smithy-client/lib/smithy-client/param_converter.rb b/gems/smithy-client/lib/smithy-client/param_converter.rb index 632e002c8..6dc819e3e 100644 --- a/gems/smithy-client/lib/smithy-client/param_converter.rb +++ b/gems/smithy-client/lib/smithy-client/param_converter.rb @@ -84,7 +84,7 @@ def union(ref, values) if values.is_a?(Schema::Union) _name, member_ref = ref.shape.member_by_type(values.class) values = shape(member_ref, values) - else + elsif values.is_a?(Hash) key, value = values.first values[key] = shape(ref.shape.member(key), value) end diff --git a/gems/smithy-client/lib/smithy-client/param_validator.rb b/gems/smithy-client/lib/smithy-client/param_validator.rb index 102cb3973..6959ebbc1 100644 --- a/gems/smithy-client/lib/smithy-client/param_validator.rb +++ b/gems/smithy-client/lib/smithy-client/param_validator.rb @@ -150,7 +150,7 @@ def union(ref, values, errors, context) if values.is_a?(Schema::Union) _name, member_ref = ref.shape.member_by_type(values.class) shape(member_ref, values.value, errors, context) - else + elsif values.is_a?(Hash) values.each_pair do |name, value| next if value.nil? diff --git a/gems/smithy-client/lib/smithy-client/plugins/pageable_response.rb b/gems/smithy-client/lib/smithy-client/plugins/pageable_response.rb index a50b81a41..0d01186f7 100644 --- a/gems/smithy-client/lib/smithy-client/plugins/pageable_response.rb +++ b/gems/smithy-client/lib/smithy-client/plugins/pageable_response.rb @@ -8,6 +8,7 @@ class PageableResponse < Plugin # @api private class Handler < Client::Handler def call(context) + context[:original_params] = context.params response = @handler.call(context) response.extend(Client::PageableResponse) response.paginator = context.operation[:paginator] || NullPaginator.new diff --git a/gems/smithy/lib/smithy/views/client/client.rb b/gems/smithy/lib/smithy/views/client/client.rb index b401f1aef..81d1fe2e7 100644 --- a/gems/smithy/lib/smithy/views/client/client.rb +++ b/gems/smithy/lib/smithy/views/client/client.rb @@ -28,7 +28,7 @@ def module_name end def service_shape - Model::Shape.name(@service_id).camelize + Model::Shape.name(@service_id) end def identifier diff --git a/gems/smithy/lib/smithy/views/client/module.rb b/gems/smithy/lib/smithy/views/client/module.rb index cce720050..7b9194d0c 100644 --- a/gems/smithy/lib/smithy/views/client/module.rb +++ b/gems/smithy/lib/smithy/views/client/module.rb @@ -58,9 +58,11 @@ def relative_requires # types must come before schemas return %i[customizations types schema] if @plan.type == :schema + # types must come before schemas # paginators must come before schemas - %w[types paginators schema auth_parameters auth_resolver client customizations errors endpoint_parameters - endpoint_provider waiters] + # customizations must come last + %w[types paginators schema auth_parameters auth_resolver client errors + endpoint_parameters endpoint_provider waiters customizations] end private diff --git a/gems/smithy/lib/smithy/views/client/schema.rb b/gems/smithy/lib/smithy/views/client/schema.rb index 6df4f9b95..dcfb91c9f 100644 --- a/gems/smithy/lib/smithy/views/client/schema.rb +++ b/gems/smithy/lib/smithy/views/client/schema.rb @@ -72,7 +72,7 @@ def initialize(service) attr_reader :id, :version, :traits def name - Model::Shape.name(@id).camelize + Model::Shape.name(@id) end end From bf4919cfd8b200f74dbd759ff4a1393bbdeba737 Mon Sep 17 00:00:00 2001 From: Matt Muller Date: Tue, 11 Nov 2025 13:05:46 -0500 Subject: [PATCH 2/7] Fix specs --- gems/smithy-client/lib/smithy-client/log_param_formatter.rb | 2 +- gems/smithy-client/lib/smithy-client/pageable_response.rb | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/gems/smithy-client/lib/smithy-client/log_param_formatter.rb b/gems/smithy-client/lib/smithy-client/log_param_formatter.rb index 96c03d09e..45556446e 100644 --- a/gems/smithy-client/lib/smithy-client/log_param_formatter.rb +++ b/gems/smithy-client/lib/smithy-client/log_param_formatter.rb @@ -46,7 +46,7 @@ def hash(hash) "#{key}: #{summarize(value)}" end end.join(', ') - "{#{res}}" + "{ #{res} }" end def array(array) diff --git a/gems/smithy-client/lib/smithy-client/pageable_response.rb b/gems/smithy-client/lib/smithy-client/pageable_response.rb index 8ccdc0a3c..970fb9103 100644 --- a/gems/smithy-client/lib/smithy-client/pageable_response.rb +++ b/gems/smithy-client/lib/smithy-client/pageable_response.rb @@ -132,7 +132,6 @@ def next_page_params(params) # Sometimes a token can be nil and merge would not include it. new_params = context[:original_params].except(*prev_tokens) new_params.merge!(@paginator.next_tokens(data).merge(params)) - new_params end end end From dee5c6b488b67168a9edfd7b78d5157c1bda4910 Mon Sep 17 00:00:00 2001 From: Matt Muller Date: Tue, 11 Nov 2025 14:47:03 -0500 Subject: [PATCH 3/7] Refactor retry exponential backoff --- .../lib/smithy-client/plugins/retry_errors.rb | 30 ++++++++++++++--- gems/smithy-client/lib/smithy-client/retry.rb | 32 ++----------------- .../lib/smithy-client/retry/adaptive.rb | 7 ++-- .../retry/exponential_backoff.rb | 23 +++++++++++++ .../lib/smithy-client/retry/standard.rb | 7 ++-- .../lib/smithy-client/retry/token.rb | 23 +++++++++++++ .../plugins/retry_errors_spec.rb | 2 +- 7 files changed, 85 insertions(+), 39 deletions(-) create mode 100644 gems/smithy-client/lib/smithy-client/retry/exponential_backoff.rb create mode 100644 gems/smithy-client/lib/smithy-client/retry/token.rb diff --git a/gems/smithy-client/lib/smithy-client/plugins/retry_errors.rb b/gems/smithy-client/lib/smithy-client/plugins/retry_errors.rb index 0c23b4133..16774ab68 100644 --- a/gems/smithy-client/lib/smithy-client/plugins/retry_errors.rb +++ b/gems/smithy-client/lib/smithy-client/plugins/retry_errors.rb @@ -33,15 +33,37 @@ class RetryErrors < Plugin DOCS option( - :retry_backoff, - default: Retry::EXPONENTIAL_BACKOFF, - doc_default: 'Smithy::Client::Retry::EXPONENTIAL_BACKOFF', - doc_type: 'lambda', + :retry_max_delay, + default: 20, + docstring: <<~DOCS) + The maximum delay, in seconds, between retry attempts. This option is ignored + if a custom `retry_backoff` is provided. Used in the `standard` and `adaptive` + retry strategies. + DOCS + + option( + :retry_base_delay, + default: 2, docstring: <<~DOCS) + The base delay, in seconds, used to calculate the exponential backoff for + retry attempts. This option is ignored if a custom `retry_backoff` is provided. + Used in the `standard` and `adaptive` retry strategies. + DOCS + + option( + :retry_backoff, + doc_default: 'Smithy::Client::Retry::ExponentialBackoff.new', + doc_type: '#call(attempts)', + docstring: <<~DOCS) do |config| A callable object that calculates a backoff delay for a retry attempt. The callable should accept a single argument, `attempts`, that represents the number of attempts that have been made. Used in the `standard` and `adaptive` retry strategies. DOCS + Retry::ExponentialBackoff.new( + retry_base_delay: config.retry_base_delay, + retry_max_delay: config.retry_max_delay + ) + end option( :adaptive_retry_wait_to_fill, diff --git a/gems/smithy-client/lib/smithy-client/retry.rb b/gems/smithy-client/lib/smithy-client/retry.rb index a1c7a946a..f785bbe10 100644 --- a/gems/smithy-client/lib/smithy-client/retry.rb +++ b/gems/smithy-client/lib/smithy-client/retry.rb @@ -2,35 +2,7 @@ require_relative 'retry/adaptive' require_relative 'retry/client_rate_limiter' +require_relative 'retry/exponential_backoff' require_relative 'retry/quota' require_relative 'retry/standard' - -module Smithy - module Client - module Retry - # The maximum backoff delay for retrying requests. - MAX_BACKOFF = 20 - - # The default backoff for retrying requests. - EXPONENTIAL_BACKOFF = lambda do |attempts| - [Kernel.rand * (2**attempts), MAX_BACKOFF].min || 0 - end - - # Represents a token that can be used to retry an operation. - class Token - def initialize - @retry_count = 0 - @retry_delay = 0 - end - - # The number of times the operation has been retried. - # @return [Integer] - attr_accessor :retry_count - - # The delay before the next retry. - # @return [Numeric] - attr_accessor :retry_delay - end - end - end -end +require_relative 'retry/token' diff --git a/gems/smithy-client/lib/smithy-client/retry/adaptive.rb b/gems/smithy-client/lib/smithy-client/retry/adaptive.rb index 090678c34..c9ae0403d 100644 --- a/gems/smithy-client/lib/smithy-client/retry/adaptive.rb +++ b/gems/smithy-client/lib/smithy-client/retry/adaptive.rb @@ -5,7 +5,7 @@ module Client module Retry # Adaptive retry strategy for retrying requests. class Adaptive - # @option [#call] :backoff (EXPONENTIAL_BACKOFF) A callable object that + # @option [#call] :backoff (ExponentialBackoff.new) A callable object that # calculates a backoff delay for a retry attempt. # @option [Integer] :max_attempts (3) The maximum number of attempts that # will be made for a single request, including the initial attempt. @@ -15,7 +15,10 @@ class Adaptive # not retry instead of sleeping. def initialize(options = {}) super() - @backoff = options[:backoff] || EXPONENTIAL_BACKOFF + @backoff = options[:backoff] || ExponentialBackoff.new( + base_delay: options[:base_delay], + max_delay: options[:max_delay] + ) @max_attempts = options[:max_attempts] || 3 @wait_to_fill = options[:wait_to_fill] || true @client_rate_limiter = ClientRateLimiter.new diff --git a/gems/smithy-client/lib/smithy-client/retry/exponential_backoff.rb b/gems/smithy-client/lib/smithy-client/retry/exponential_backoff.rb new file mode 100644 index 000000000..795d5d99e --- /dev/null +++ b/gems/smithy-client/lib/smithy-client/retry/exponential_backoff.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Smithy + module Client + module Retry + # Default exponential backoff retry strategy for retrying requests. + class ExponentialBackoff + def initialize(options = {}) + @base_delay = options[:base_delay] || 2 + @max_delay = options[:max_delay] || 20 + end + + # Calculates a delay based on exponential backoff strategy. Uses full jitter approach. + # @param [Integer] attempts + # @return [Float] delay in seconds + def call(attempts) + delay = (@base_delay**attempts) + [delay, @max_delay].min * Kernel.rand + end + end + end + end +end diff --git a/gems/smithy-client/lib/smithy-client/retry/standard.rb b/gems/smithy-client/lib/smithy-client/retry/standard.rb index 3418ef3a3..78240d21e 100644 --- a/gems/smithy-client/lib/smithy-client/retry/standard.rb +++ b/gems/smithy-client/lib/smithy-client/retry/standard.rb @@ -5,13 +5,16 @@ module Client module Retry # Standard retry strategy for retrying requests. class Standard - # @option [#call] :backoff (EXPONENTIAL_BACKOFF) A callable object that + # @option [#call] :backoff (ExponentialBackoff.new) A callable object that # calculates a backoff delay for a retry attempt. # @option [Integer] :max_attempts (3) The maximum number of attempts that # will be made for a single request, including the initial attempt. def initialize(options = {}) super() - @backoff = options[:backoff] || EXPONENTIAL_BACKOFF + @backoff = options[:backoff] || ExponentialBackoff.new( + base_delay: options[:base_delay], + max_delay: options[:max_delay] + ) @max_attempts = options[:max_attempts] || 3 @quota = Quota.new @capacity_amount = 0 diff --git a/gems/smithy-client/lib/smithy-client/retry/token.rb b/gems/smithy-client/lib/smithy-client/retry/token.rb new file mode 100644 index 000000000..93eb38469 --- /dev/null +++ b/gems/smithy-client/lib/smithy-client/retry/token.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Smithy + module Client + module Retry + # Represents a token that can be used to retry an operation. + class Token + def initialize + @retry_count = 0 + @retry_delay = 0 + end + + # The number of times the operation has been retried. + # @return [Integer] + attr_accessor :retry_count + + # The delay before the next retry. + # @return [Numeric] + attr_accessor :retry_delay + end + end + end +end diff --git a/gems/smithy-client/spec/smithy-client/plugins/retry_errors_spec.rb b/gems/smithy-client/spec/smithy-client/plugins/retry_errors_spec.rb index 7e85648ae..9a648c89a 100644 --- a/gems/smithy-client/spec/smithy-client/plugins/retry_errors_spec.rb +++ b/gems/smithy-client/spec/smithy-client/plugins/retry_errors_spec.rb @@ -203,8 +203,8 @@ module Plugins end it 'does not exceed the max backoff time' do + config.retry_strategy = Retry::Standard.new(max_delay: 3) retry_strategy.instance_variable_set(:@max_attempts, 5) - stub_const('Smithy::Client::Retry::MAX_BACKOFF', 3) test_case_def = [ { From 3f9c2605742d90b6389b492d7a44e2369a9e7e0c Mon Sep 17 00:00:00 2001 From: Matt Muller Date: Tue, 11 Nov 2025 14:57:45 -0500 Subject: [PATCH 4/7] Reconstantize --- gems/smithy/lib/smithy/views/client/client.rb | 2 +- gems/smithy/lib/smithy/views/client/schema.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gems/smithy/lib/smithy/views/client/client.rb b/gems/smithy/lib/smithy/views/client/client.rb index 81d1fe2e7..b401f1aef 100644 --- a/gems/smithy/lib/smithy/views/client/client.rb +++ b/gems/smithy/lib/smithy/views/client/client.rb @@ -28,7 +28,7 @@ def module_name end def service_shape - Model::Shape.name(@service_id) + Model::Shape.name(@service_id).camelize end def identifier diff --git a/gems/smithy/lib/smithy/views/client/schema.rb b/gems/smithy/lib/smithy/views/client/schema.rb index dcfb91c9f..6df4f9b95 100644 --- a/gems/smithy/lib/smithy/views/client/schema.rb +++ b/gems/smithy/lib/smithy/views/client/schema.rb @@ -72,7 +72,7 @@ def initialize(service) attr_reader :id, :version, :traits def name - Model::Shape.name(@id) + Model::Shape.name(@id).camelize end end From 97fdc419bea5ffd09e7408b0e7c62b8814bd5cef Mon Sep 17 00:00:00 2001 From: Matt Muller Date: Tue, 11 Nov 2025 15:01:50 -0500 Subject: [PATCH 5/7] Rebuild --- projections/shapes/lib/shapes.rb | 2 +- projections/shapes/lib/shapes/client.rb | 10 +++++++++- projections/shapes/sig/shapes/client.rbs | 4 +++- projections/weather/lib/weather.rb | 2 +- projections/weather/lib/weather/client.rb | 10 +++++++++- projections/weather/sig/weather/client.rbs | 4 +++- 6 files changed, 26 insertions(+), 6 deletions(-) diff --git a/projections/shapes/lib/shapes.rb b/projections/shapes/lib/shapes.rb index 116444c06..c5b7e93f7 100644 --- a/projections/shapes/lib/shapes.rb +++ b/projections/shapes/lib/shapes.rb @@ -16,8 +16,8 @@ module ShapeService require_relative 'shapes/auth_parameters' require_relative 'shapes/auth_resolver' require_relative 'shapes/client' -require_relative 'shapes/customizations' require_relative 'shapes/errors' require_relative 'shapes/endpoint_parameters' require_relative 'shapes/endpoint_provider' require_relative 'shapes/waiters' +require_relative 'shapes/customizations' diff --git a/projections/shapes/lib/shapes/client.rb b/projections/shapes/lib/shapes/client.rb index 68c72a96e..43823faab 100644 --- a/projections/shapes/lib/shapes/client.rb +++ b/projections/shapes/lib/shapes/client.rb @@ -140,13 +140,21 @@ class Client < Smithy::Client::Base # @option options [Integer] :request_min_compression_size_bytes (10240) # The minimum size in bytes that triggers compression for request bodies. # The value must be non-negative integer value between 0 and 10,485,780 bytes inclusive. - # @option options [lambda] :retry_backoff (Smithy::Client::Retry::EXPONENTIAL_BACKOFF) + # @option options [#call(attempts)] :retry_backoff (Smithy::Client::Retry::ExponentialBackoff.new) # A callable object that calculates a backoff delay for a retry attempt. The callable # should accept a single argument, `attempts`, that represents the number of attempts # that have been made. Used in the `standard` and `adaptive` retry strategies. + # @option options :retry_base_delay (2) + # The base delay, in seconds, used to calculate the exponential backoff for + # retry attempts. This option is ignored if a custom `retry_backoff` is provided. + # Used in the `standard` and `adaptive` retry strategies. # @option options [Integer] :retry_max_attempts (3) # The maximum number attempts that will be made for a single request, including # the initial attempt. Used in the `standard` and `adaptive` retry strategies. + # @option options :retry_max_delay (20) + # The maximum delay, in seconds, between retry attempts. This option is ignored + # if a custom `retry_backoff` is provided. Used in the `standard` and `adaptive` + # retry strategies. # @option options [String, Class] :retry_strategy ('standard') # The retry strategy to use when retrying errors. This can be one of the following: # * `standard` - A standardized retry strategy used by the AWS SDKs. This includes support diff --git a/projections/shapes/sig/shapes/client.rbs b/projections/shapes/sig/shapes/client.rbs index 6bd364e69..41eb50040 100644 --- a/projections/shapes/sig/shapes/client.rbs +++ b/projections/shapes/sig/shapes/client.rbs @@ -30,8 +30,10 @@ module ShapeService ?logger: Logger, ?raise_response_errors: bool, ?request_min_compression_size_bytes: Integer, - ?retry_backoff: lambda, + ?retry_backoff: #call(attempts), + ?retry_base_delay: untyped, ?retry_max_attempts: Integer, + ?retry_max_delay: untyped, ?retry_strategy: String | Class, ?stub_responses: bool, ?user_agent_suffix: String, diff --git a/projections/weather/lib/weather.rb b/projections/weather/lib/weather.rb index ecfdd9db0..e0ea6f17d 100644 --- a/projections/weather/lib/weather.rb +++ b/projections/weather/lib/weather.rb @@ -17,8 +17,8 @@ module Weather require_relative 'weather/auth_parameters' require_relative 'weather/auth_resolver' require_relative 'weather/client' -require_relative 'weather/customizations' require_relative 'weather/errors' require_relative 'weather/endpoint_parameters' require_relative 'weather/endpoint_provider' require_relative 'weather/waiters' +require_relative 'weather/customizations' diff --git a/projections/weather/lib/weather/client.rb b/projections/weather/lib/weather/client.rb index 1136a3d93..80efaf90b 100644 --- a/projections/weather/lib/weather/client.rb +++ b/projections/weather/lib/weather/client.rb @@ -140,13 +140,21 @@ class Client < Smithy::Client::Base # @option options [Integer] :request_min_compression_size_bytes (10240) # The minimum size in bytes that triggers compression for request bodies. # The value must be non-negative integer value between 0 and 10,485,780 bytes inclusive. - # @option options [lambda] :retry_backoff (Smithy::Client::Retry::EXPONENTIAL_BACKOFF) + # @option options [#call(attempts)] :retry_backoff (Smithy::Client::Retry::ExponentialBackoff.new) # A callable object that calculates a backoff delay for a retry attempt. The callable # should accept a single argument, `attempts`, that represents the number of attempts # that have been made. Used in the `standard` and `adaptive` retry strategies. + # @option options :retry_base_delay (2) + # The base delay, in seconds, used to calculate the exponential backoff for + # retry attempts. This option is ignored if a custom `retry_backoff` is provided. + # Used in the `standard` and `adaptive` retry strategies. # @option options [Integer] :retry_max_attempts (3) # The maximum number attempts that will be made for a single request, including # the initial attempt. Used in the `standard` and `adaptive` retry strategies. + # @option options :retry_max_delay (20) + # The maximum delay, in seconds, between retry attempts. This option is ignored + # if a custom `retry_backoff` is provided. Used in the `standard` and `adaptive` + # retry strategies. # @option options [String, Class] :retry_strategy ('standard') # The retry strategy to use when retrying errors. This can be one of the following: # * `standard` - A standardized retry strategy used by the AWS SDKs. This includes support diff --git a/projections/weather/sig/weather/client.rbs b/projections/weather/sig/weather/client.rbs index 003fe6ae7..407767816 100644 --- a/projections/weather/sig/weather/client.rbs +++ b/projections/weather/sig/weather/client.rbs @@ -30,8 +30,10 @@ module Weather ?logger: Logger, ?raise_response_errors: bool, ?request_min_compression_size_bytes: Integer, - ?retry_backoff: lambda, + ?retry_backoff: #call(attempts), + ?retry_base_delay: untyped, ?retry_max_attempts: Integer, + ?retry_max_delay: untyped, ?retry_strategy: String | Class, ?stub_responses: bool, ?user_agent_suffix: String, From e82f0e164a6f4c7832b5e17e26a0a1ab7cdc7830 Mon Sep 17 00:00:00 2001 From: Matt Muller Date: Tue, 11 Nov 2025 15:14:49 -0500 Subject: [PATCH 6/7] Fix rbs tests --- .../lib/smithy-client/plugins/retry_errors.rb | 1 + .../lib/smithy-client/retry/exponential_backoff.rb | 8 +++++++- .../sig/smithy-client/retry/exponential_backoff.rbs | 12 ++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 gems/smithy-client/sig/smithy-client/retry/exponential_backoff.rbs diff --git a/gems/smithy-client/lib/smithy-client/plugins/retry_errors.rb b/gems/smithy-client/lib/smithy-client/plugins/retry_errors.rb index 16774ab68..720d85137 100644 --- a/gems/smithy-client/lib/smithy-client/plugins/retry_errors.rb +++ b/gems/smithy-client/lib/smithy-client/plugins/retry_errors.rb @@ -53,6 +53,7 @@ class RetryErrors < Plugin option( :retry_backoff, doc_default: 'Smithy::Client::Retry::ExponentialBackoff.new', + rbs_type: 'Smithy::Client::Retry::ExponentialBackoff', doc_type: '#call(attempts)', docstring: <<~DOCS) do |config| A callable object that calculates a backoff delay for a retry attempt. The callable diff --git a/gems/smithy-client/lib/smithy-client/retry/exponential_backoff.rb b/gems/smithy-client/lib/smithy-client/retry/exponential_backoff.rb index 795d5d99e..76624b3c5 100644 --- a/gems/smithy-client/lib/smithy-client/retry/exponential_backoff.rb +++ b/gems/smithy-client/lib/smithy-client/retry/exponential_backoff.rb @@ -10,9 +10,15 @@ def initialize(options = {}) @max_delay = options[:max_delay] || 20 end + # @return [Numeric] + attr_reader :base_delay + + # @return [Numeric] + attr_reader :max_delay + # Calculates a delay based on exponential backoff strategy. Uses full jitter approach. # @param [Integer] attempts - # @return [Float] delay in seconds + # @return [Numeric] delay in seconds def call(attempts) delay = (@base_delay**attempts) [delay, @max_delay].min * Kernel.rand diff --git a/gems/smithy-client/sig/smithy-client/retry/exponential_backoff.rbs b/gems/smithy-client/sig/smithy-client/retry/exponential_backoff.rbs new file mode 100644 index 000000000..7fe3fdec2 --- /dev/null +++ b/gems/smithy-client/sig/smithy-client/retry/exponential_backoff.rbs @@ -0,0 +1,12 @@ +module Smithy + module Client + module Retry + class ExponentialBackoff + def initialize: (?Hash[Symbol, untyped] options) -> void + attr_reader base_delay: Numeric + attr_reader max_delay: Numeric + def call: (Integer attempts) -> Numeric + end + end + end +end From 78a14de7b25805d1bc8b2a56315e188e17956525 Mon Sep 17 00:00:00 2001 From: Matt Muller Date: Sat, 22 Nov 2025 10:51:23 -0500 Subject: [PATCH 7/7] Minor doc update --- gems/smithy-client/lib/smithy-client/handler_list.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/gems/smithy-client/lib/smithy-client/handler_list.rb b/gems/smithy-client/lib/smithy-client/handler_list.rb index bdb97c5dd..2978c099b 100644 --- a/gems/smithy-client/lib/smithy-client/handler_list.rb +++ b/gems/smithy-client/lib/smithy-client/handler_list.rb @@ -55,7 +55,6 @@ def entries # * `:validate` # * `:build` # * `:retry` - # * `:parse` # * `:sign` # * `:send` # @@ -86,7 +85,6 @@ def entries # * `:validate` # * `:build` # * `:retry` - # * `:parse` # * `:sign` # * `:send` #