Skip to content

Commit

Permalink
Merge pull request #48793 from Shopify/define-attributes-initializer
Browse files Browse the repository at this point in the history
Never connect to the database in define_attribute_methods initializer
  • Loading branch information
byroot committed Aug 1, 2023
2 parents 741cd18 + de765e3 commit 35a614c
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 24 deletions.
22 changes: 10 additions & 12 deletions activerecord/lib/active_record/railtie.rb
Expand Up @@ -186,20 +186,18 @@ class Railtie < Rails::Railtie # :nodoc:
ActiveSupport.on_load(:active_record) do
# In development and test we shouldn't eagerly define attribute methods because
# db:test:prepare will trigger later and might change the schema.
if app.config.eager_load && !Rails.env.local?
#
# Additionally if `check_schema_cache_dump_version` is enabled (which is the default),
# loading the schema cache dump trigger a database connection to compare the schema
# versions.
# This means the attribute methods will be lazily defined whent the model is accessed,
# likely as part of the first few requests or jobs. This isn't good for performance
# but we unfortunately have to arbitrate between resiliency and performance, and chose
# resiliency.
if !check_schema_cache_dump_version && app.config.eager_load && !Rails.env.local?
begin
descendants.each do |model|
# If the schema cache doesn't have the columns for this model,
# we avoid calling `define_attribute_methods` as it would trigger a query.
#
# However if we're already connected to the database, it's too late so we might
# as well eagerly define the attributes and hope the database timeout is strict enough.
#
# Additionally if `check_schema_cache_dump_version` is enabled, we have to connect to the
# database anyway to load the schema cache dump, so we might as well do it during boot to
# save memory in pre-forking setups and avoid slowness during the first requests post deploy.
schema_reflection = model.connection_pool.schema_reflection
if check_schema_cache_dump_version || schema_reflection.cached?(model.table_name) || model.connected?
if model.connection_pool.schema_reflection.cached?(model.table_name)
model.define_attribute_methods
end
end
Expand Down
24 changes: 12 additions & 12 deletions railties/test/application/configuration_test.rb
Expand Up @@ -406,7 +406,7 @@ class Post < ActiveRecord::Base
assert_not_includes Post.instance_methods, :title
end

test "does not eager load attribute methods in production when the schema cache is empty and the database not connected" do
test "does not eager load attribute methods in production when the schema cache is empty and and check_schema_cache_dump_version=false" do
app_file "app/models/post.rb", <<-RUBY
class Post < ActiveRecord::Base
end
Expand All @@ -423,7 +423,7 @@ class Post < ActiveRecord::Base
assert_not_includes (Post.instance_methods - ActiveRecord::Base.instance_methods), :title
end

test "eager loads attribute methods in production when the schema cache is populated" do
test "eager loads attribute methods in production when the schema cache is populated and check_schema_cache_dump_version=false" do
app_file "app/models/post.rb", <<-RUBY
class Post < ActiveRecord::Base
end
Expand All @@ -442,18 +442,19 @@ class Post < ActiveRecord::Base
add_to_config <<-RUBY
config.enable_reloading = false
config.eager_load = true
config.active_record.check_schema_cache_dump_version = false
RUBY

app_file "config/initializers/schema_cache.rb", <<-RUBY
ActiveRecord::Base.connection.schema_cache.add("posts")
ActiveRecord::Base.connection.schema_cache.add("posts")
RUBY

app "production"

assert_includes Post.instance_methods, :title
assert_includes (Post.instance_methods - ActiveRecord::Base.instance_methods), :title
end

test "does not attempt to eager load attribute methods for models that aren't connected" do
test "does not eager loads attribute methods in production when the schema cache is populated and check_schema_cache_dump_version=true" do
app_file "app/models/post.rb", <<-RUBY
class Post < ActiveRecord::Base
end
Expand All @@ -472,17 +473,16 @@ class Post < ActiveRecord::Base
add_to_config <<-RUBY
config.enable_reloading = false
config.eager_load = true
config.active_record.check_schema_cache_dump_version = true
RUBY

app_file "app/models/comment.rb", <<-RUBY
class Comment < ActiveRecord::Base
establish_connection(adapter: "mysql2", database: "does_not_exist")
end
app_file "config/initializers/schema_cache.rb", <<-RUBY
ActiveRecord::Base.connection.schema_cache.add("posts")
RUBY

assert_nothing_raised do
app "production"
end
app "production"

assert_not_includes (Post.instance_methods - ActiveRecord::Base.instance_methods), :title
end

test "application is always added to eager_load namespaces" do
Expand Down

0 comments on commit 35a614c

Please sign in to comment.