diff --git a/gems/smithy-client/lib/smithy-client.rb b/gems/smithy-client/lib/smithy-client.rb index c85cea37b..c3b8be960 100644 --- a/gems/smithy-client/lib/smithy-client.rb +++ b/gems/smithy-client/lib/smithy-client.rb @@ -14,6 +14,7 @@ require_relative 'smithy-client/default_params' require_relative 'smithy-client/dynamic_errors' require_relative 'smithy-client/endpoint_rules' +require_relative 'smithy-client/features' require_relative 'smithy-client/handler' require_relative 'smithy-client/handler_builder' require_relative 'smithy-client/handler_context' diff --git a/gems/smithy-client/lib/smithy-client/features.rb b/gems/smithy-client/lib/smithy-client/features.rb new file mode 100644 index 000000000..f60321fd6 --- /dev/null +++ b/gems/smithy-client/lib/smithy-client/features.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Smithy + module Client + # @api private + module Features + class << self + def track(*features, &block) + Thread.current[:smithy_ruby_features] ||= [] + Thread.current[:smithy_ruby_features].concat(features) + block.call + ensure + Thread.current[:smithy_ruby_features].pop(features.size) + end + + def tracked + Thread.current[:smithy_ruby_features] || [] + end + end + end + end +end diff --git a/gems/smithy-client/lib/smithy-client/pageable_response.rb b/gems/smithy-client/lib/smithy-client/pageable_response.rb index 99b5e8844..2f9fa1046 100644 --- a/gems/smithy-client/lib/smithy-client/pageable_response.rb +++ b/gems/smithy-client/lib/smithy-client/pageable_response.rb @@ -92,7 +92,7 @@ def next_page(params = {}) raise LastPageError, self if last_page? params = next_page_params(params) - context.client.send(context.operation_name, params) + Features.track('PAGINATOR') { context.client.send(context.operation_name, params) } end # Yields the current and each following response to the given block. diff --git a/gems/smithy-client/lib/smithy-client/plugins/request_compression.rb b/gems/smithy-client/lib/smithy-client/plugins/request_compression.rb index a9c5653d0..953f3d57f 100644 --- a/gems/smithy-client/lib/smithy-client/plugins/request_compression.rb +++ b/gems/smithy-client/lib/smithy-client/plugins/request_compression.rb @@ -77,7 +77,7 @@ def call(context) end end end - @handler.call(context) + track_feature(selected_encoding) { @handler.call(context) } end private @@ -149,6 +149,15 @@ def update_content_encoding(encoding, context) end end + def track_feature(encoding, &block) + case encoding + when 'gzip' + Features.track('GZIP_REQUEST_COMPRESSION', &block) + else + block.call + end + end + # @api private class GzipIO def initialize(body) 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 915af5865..3c6561452 100644 --- a/gems/smithy-client/lib/smithy-client/plugins/retry_errors.rb +++ b/gems/smithy-client/lib/smithy-client/plugins/retry_errors.rb @@ -84,7 +84,7 @@ def call(context) private def handle(context, retry_strategy, token) - response = @handler.call(context) + response = track_feature(retry_strategy) { @handler.call(context) } if (error = response.error) return response unless retryable?(context.http_request) @@ -117,6 +117,14 @@ def reset_response(context, response) context.http_response.reset response.error = nil end + + def track_feature(retry_strategy, &block) + case retry_strategy + when Retry::Standard then Features.track('RETRY_MODE_STANDARD', &block) + when Retry::Adaptive then Features.track('RETRY_MODE_ADAPTIVE', &block) + else block.call + end + end end handler(Handler, step: :retry) diff --git a/gems/smithy-client/lib/smithy-client/waiters/poller.rb b/gems/smithy-client/lib/smithy-client/waiters/poller.rb index 3fdd25649..74e2b7583 100644 --- a/gems/smithy-client/lib/smithy-client/waiters/poller.rb +++ b/gems/smithy-client/lib/smithy-client/waiters/poller.rb @@ -15,7 +15,7 @@ def call(client, params) @input = params request = client.build_request(@operation_name, params) request.handlers.remove(Plugins::RaiseResponseErrors::Handler) - response = request.send_request + response = Features.track('WAITER') { request.send_request } status = evaluate_acceptors(response) [response, status.to_sym] end diff --git a/gems/smithy-client/spec/smithy-client/features_spec.rb b/gems/smithy-client/spec/smithy-client/features_spec.rb new file mode 100644 index 000000000..6f89bbaed --- /dev/null +++ b/gems/smithy-client/spec/smithy-client/features_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require_relative '../spec_helper' + +module Smithy + module Client + describe Features do + it 'tracks and removes a feature' do + Features.track('A') { expect(Features.tracked).to eq(%w[A]) } + expect(Features.tracked).to be_empty + end + + it 'tracks and removes multiple features' do + features = %w[A B C] + Features.track(*features) { expect(Features.tracked).to eq(features) } + expect(Features.tracked).to be_empty + end + + it 'tracks and removes features in stack order' do + Features.track('A') do + expect(Features.tracked).to eq(%w[A]) + Features.track('B') do + expect(Features.tracked).to eq(%w[A B]) + Features.track('C') do + expect(Features.tracked).to eq(%w[A B C]) + end + expect(Features.tracked).to eq(%w[A B]) + end + expect(Features.tracked).to eq(%w[A]) + end + expect(Features.tracked).to be_empty + end + + it 'ensures that features are removed' do + begin + Features.track('A') do + expect(Features.tracked).to eq(%w[A]) + raise StandardError + end + rescue StandardError + # ignore + end + expect(Features.tracked).to be_empty + end + + it 'tracks features in multiple threads' do + Features.track('A') do + expect(Features.tracked).to eq(%w[A]) + Thread.new do + expect(Features.tracked).to be_empty + Features.track('B') do + expect(Features.tracked).to eq(%w[B]) + end + expect(Features.tracked).to be_empty + end.join + expect(Features.tracked).to eq(%w[A]) + end + expect(Features.tracked).to be_empty + end + end + end +end