Eager load controller and mailer actions

On the first request, ActionController::Base#action_methods computes
and memoized the list of available actions [1]. With this PR we move
this expensive operation into eager load step to reduce response time
of the first request served in production.

This also reduces the memory footprint when running on forking server
like Unicorn.

[1] a3813dce9a/actionpack/lib/abstract_controller/base.rb (L66-L77)
This commit is contained in:
Kir Shatrov 2017-06-24 10:57:36 -04:00
parent 904f1a8747
commit 0668c22a41
3 changed files with 72 additions and 0 deletions

View File

@ -58,6 +58,12 @@ module ActionMailer
end
end
initializer "action_mailer.eager_load_actions" do
ActiveSupport.on_load(:after_initialize) do
ActionMailer::Base.descendants.each(&:action_methods) if config.eager_load
end
end
config.after_initialize do |app|
options = app.config.action_mailer

View File

@ -77,5 +77,11 @@ module ActionController
end
end
end
initializer "action_controller.eager_load_actions" do
ActiveSupport.on_load(:after_initialize) do
ActionController::Metal.descendants.each(&:action_methods) if config.eager_load
end
end
end
end

View File

@ -238,6 +238,66 @@ module ApplicationTests
assert_instance_of Pathname, Rails.public_path
end
test "does not eager load controller actions in development" do
app_file "app/controllers/posts_controller.rb", <<-RUBY
class PostsController < ActionController::Base
def index;end
def show;end
end
RUBY
app "development"
assert_nil PostsController.instance_variable_get(:@action_methods)
end
test "eager loads controller actions in production" do
app_file "app/controllers/posts_controller.rb", <<-RUBY
class PostsController < ActionController::Base
def index;end
def show;end
end
RUBY
add_to_config <<-RUBY
config.eager_load = true
config.cache_classes = true
RUBY
app "production"
assert_equal %w(index show).to_set, PostsController.instance_variable_get(:@action_methods)
end
test "does not eager load mailer actions in development" do
app_file "app/mailers/posts_mailer.rb", <<-RUBY
class PostsMailer < ActionMailer::Base
def noop_email;end
end
RUBY
app "development"
assert_nil PostsMailer.instance_variable_get(:@action_methods)
end
test "eager loads mailer actions in production" do
app_file "app/mailers/posts_mailer.rb", <<-RUBY
class PostsMailer < ActionMailer::Base
def noop_email;end
end
RUBY
add_to_config <<-RUBY
config.eager_load = true
config.cache_classes = true
RUBY
app "production"
assert_equal %w(noop_email).to_set, PostsMailer.instance_variable_get(:@action_methods)
end
test "initialize an eager loaded, cache classes app" do
add_to_config <<-RUBY
config.eager_load = true