Conversation
…ding each response Kernel#extend gives the response a singleton class and clears the method cache for every method the extension defines (each, count, to_h, respond_to?, and all of Enumerable), process-wide, on every request. Under YJIT that discards every compiled block that relied on one of those names being absent; a Rails app signing one S3 URL per request measured ~4 invalidations and 2.3 KB of regenerated code per URL, and its workers reached --yjit-mem-size within hours and stopped compiling. Including the extension in the response class once keeps the same interface on every response with no per-response work. apply still extends other objects, so its constant cache guarantee is unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
Aws::PageableResponse.applyextends everySeahorse::Client::ResponsewithPageableResponse::Extension.Kernel#extendgives the object a singleton class and clears Ruby's method cache for every method the module defines (each,count,to_h,to_json,as_json,respond_to?, and all ofEnumerableviainclude Enumerable), process-wide, on every request. Rails and its gems ask objectsrespond_to?(:each)/respond_to?(:to_h)constantly, so under YJIT each request discards every compiled block that assumed one of those names was absent.This is the method-cache twin of #2670 (which moved the constants out of the module so
extendwould not bust the constant cache); the comment onExtensionstill explains that reasoning, and this change applies the same thinking to the method cache.Measured in a Rails app that signs one S3 URL per request (the presigner builds a response through the same handler stack):
invalidate_method_lookupIn production, 10 Puma workers on Ruby 4.0.6 with YJIT reached ~122 MiB of the default 128 MiB
--yjit-mem-sizeafter nine hours, with ~3 invalidations a second per worker and 42% of all blocks ever compiled invalidated; at the limit YJIT stops compiling. A gdb breakpoint onrb_yjit_cme_invalidatetraced every hit torb_obj_extend → rb_include_module → invalidate_negative_cache.The change mixes
ExtensionintoSeahorse::Client::Responseonce, whenPageableResponseloads, and makesapplyskipextendfor an object whose class already includes it. Every response keeps the same interface (each,next_page?,count, …) andapplystill works on arbitrary objects, so the existing constant-cache spec is unchanged. The new spec asserts that applying to a response creates noT_CLASS/T_ICLASS, and that a plainSeahorse::Client::Responseis already pageable.bundle exec rspec gems/aws-sdk-core/spec(2427 examples) andgems/aws-sdk-s3/spec(1660 examples) pass on Ruby 4.0.6.Standalone reproduction (released gem, no network):
gem install aws-sdk-s3 && ruby --yjit --yjit-stats repro.rbOutput against aws-sdk-core 3.257.0 on Ruby 4.0.6 (the 4 classes still allocated in the fixed run are the presigner's own
Class.new(Handler)blocks, unrelated to this change):Generated with AI tools (Claude Code), and reviewed by Ikraam Ghoor.