Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions gems/aws-sdk-core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Unreleased Changes
------------------

* Issue - Mix `PageableResponse` into `Seahorse::Client::Response` once instead of extending every response object, which cleared Ruby's method cache for each of the extension's methods on every request and made YJIT discard compiled code.

3.257.0 (2026-09-14)
------------------

Expand Down
7 changes: 6 additions & 1 deletion gems/aws-sdk-core/lib/aws-sdk-core/pageable_response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ module Aws
module PageableResponse

def self.apply(base)
base.extend Extension
base.extend Extension unless Extension === base
base.instance_variable_set(:@last_page, nil)
base.instance_variable_set(:@more_results, nil)
base
Expand Down Expand Up @@ -217,5 +217,10 @@ def next_page_params(params)
end

end

# Mixed into the response class once rather than extended onto each
# response: Kernel#extend clears the method cache for every method the
# extension defines, process-wide, on every request.
Seahorse::Client::Response.include(Extension)
end
end
21 changes: 21 additions & 0 deletions gems/aws-sdk-core/spec/aws/pageable_response_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,27 @@ def pageable(resp, pager)
PageableResponse.apply(object)
}.to_not change { RubyVM.stat(key) }
end

# Kernel#extend creates a singleton class and clears the method cache for every
# method the module defines (each, count, to_h, respond_to?, all of Enumerable...),
# process-wide, so it must not happen once per response.
it 'does not give a Seahorse::Client::Response a singleton class' do
skip 'Only applies to MRI' unless defined? RubyVM.stat

resp = Seahorse::Client::Response.new
GC.disable
classes_before = ObjectSpace.count_objects.values_at(:T_CLASS, :T_ICLASS)
PageableResponse.apply(resp)
expect(ObjectSpace.count_objects.values_at(:T_CLASS, :T_ICLASS)).to eq(classes_before)
ensure
GC.enable
end

it 'leaves every Seahorse::Client::Response pageable' do
resp = Seahorse::Client::Response.new
expect(resp).to be_kind_of(PageableResponse::Extension)
expect(resp).to be_kind_of(Enumerable)
end
end

end
Expand Down