mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Rescue Database errors during active_record.define_attribute_methods
This initializer should be best effort. If for some reason the database in unhealthy, it is better to move forward with the boot process than to crash.
This commit is contained in:
parent
7101489293
commit
36fae4bdaf
2 changed files with 85 additions and 11 deletions
|
@ -146,7 +146,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|||
current_version = begin
|
||||
ActiveRecord::Migrator.current_version
|
||||
rescue ActiveRecordError => error
|
||||
warn "Failed to load the schema cache because of #{error.class}: #{error.message}"
|
||||
warn "Failed to validate the schema cache because of #{error.class}: #{error.message}"
|
||||
nil
|
||||
end
|
||||
next if current_version.nil?
|
||||
|
@ -167,16 +167,29 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|||
config.after_initialize do
|
||||
ActiveSupport.on_load(:active_record) do
|
||||
if app.config.eager_load
|
||||
descendants.each do |model|
|
||||
# SchemaMigration and InternalMetadata both override `table_exists?`
|
||||
# to bypass the schema cache, so skip them to avoid the extra queries.
|
||||
next if model._internal?
|
||||
begin
|
||||
descendants.each do |model|
|
||||
# SchemaMigration and InternalMetadata both override `table_exists?`
|
||||
# to bypass the schema cache, so skip them to avoid the extra queries.
|
||||
next if model._internal?
|
||||
|
||||
# If there's no connection yet, or the schema cache doesn't have the columns
|
||||
# hash for the model cached, `define_attribute_methods` would trigger a query.
|
||||
next unless model.connected? && model.connection.schema_cache.columns_hash?(model.table_name)
|
||||
# If the schema cache was loaded from a dump, we can use it without connecting
|
||||
schema_cache = model.connection_pool.schema_cache
|
||||
|
||||
model.define_attribute_methods
|
||||
# If there's no connection yet, we avoid connecting.
|
||||
schema_cache ||= model.connected? && model.connection.schema_cache
|
||||
|
||||
# If the schema cache doesn't have the columns
|
||||
# hash for the model cached, `define_attribute_methods` would trigger a query.
|
||||
if schema_cache && schema_cache.columns_hash?(model.table_name)
|
||||
model.define_attribute_methods
|
||||
end
|
||||
end
|
||||
rescue ActiveRecordError => error
|
||||
# Regardless of wether there was already a connection or not, we rescue any database
|
||||
# error because it is critical that the application can boot even if the database
|
||||
# is unhealthy.
|
||||
warn "Failed to define attribute methods because of #{error.class}: #{error.message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "isolation/abstract_unit"
|
||||
require "env_helpers"
|
||||
|
||||
module ApplicationTests
|
||||
class FrameworksTest < ActiveSupport::TestCase
|
||||
include ActiveSupport::Testing::Isolation
|
||||
include EnvHelpers
|
||||
|
||||
def setup
|
||||
build_app
|
||||
|
@ -214,10 +216,24 @@ module ApplicationTests
|
|||
assert !defined?(ActiveRecord::Base) || ActiveRecord.autoload?(:Base)
|
||||
end
|
||||
|
||||
test "can boot with an unhealthy database" do
|
||||
rails %w(generate model post title:string)
|
||||
|
||||
switch_env("DATABASE_URL", "mysql2://127.0.0.1:1") do
|
||||
require "#{app_path}/config/environment"
|
||||
end
|
||||
end
|
||||
|
||||
test "use schema cache dump" do
|
||||
rails %w(generate model post title:string)
|
||||
rails %w(db:migrate db:schema:cache:dump)
|
||||
|
||||
add_to_config <<-RUBY
|
||||
config.eager_load = true
|
||||
RUBY
|
||||
|
||||
require "#{app_path}/config/environment"
|
||||
|
||||
assert ActiveRecord::Base.connection.schema_cache.data_sources("posts")
|
||||
ensure
|
||||
ActiveRecord::Base.connection.drop_table("posts", if_exists: true) # force drop posts table for test.
|
||||
|
@ -226,20 +242,65 @@ module ApplicationTests
|
|||
test "expire schema cache dump" do
|
||||
rails %w(generate model post title:string)
|
||||
rails %w(db:migrate db:schema:cache:dump db:rollback)
|
||||
|
||||
add_to_config <<-RUBY
|
||||
config.eager_load = true
|
||||
RUBY
|
||||
|
||||
require "#{app_path}/config/environment"
|
||||
assert_not ActiveRecord::Base.connection.schema_cache.data_sources("posts")
|
||||
end
|
||||
|
||||
test "does not expire schema cache dump if check_schema_cache_dump_version is false" do
|
||||
test "expire schema cache dump if the version can't be checked because the database is unhealthy" do
|
||||
rails %w(generate model post title:string)
|
||||
rails %w(db:migrate db:schema:cache:dump)
|
||||
|
||||
add_to_config <<-RUBY
|
||||
config.active_record.check_schema_cache_dump_version = false
|
||||
config.eager_load = true
|
||||
RUBY
|
||||
|
||||
switch_env("DATABASE_URL", "mysql2://127.0.0.1:1") do
|
||||
require "#{app_path}/config/environment"
|
||||
|
||||
assert_nil ActiveRecord::Base.connection_pool.schema_cache
|
||||
assert_raises ActiveRecord::ConnectionNotEstablished do
|
||||
ActiveRecord::Base.connection.execute("SELECT 1")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "does not expire schema cache dump if check_schema_cache_dump_version is false" do
|
||||
rails %w(generate model post title:string)
|
||||
rails %w(db:migrate db:schema:cache:dump db:rollback)
|
||||
|
||||
add_to_config <<-RUBY
|
||||
config.eager_load = true
|
||||
config.active_record.check_schema_cache_dump_version = false
|
||||
RUBY
|
||||
|
||||
require "#{app_path}/config/environment"
|
||||
assert ActiveRecord::Base.connection_pool.schema_cache.data_sources("posts")
|
||||
end
|
||||
|
||||
test "does not expire schema cache dump if check_schema_cache_dump_version is false and the database unhealthy" do
|
||||
rails %w(generate model post title:string)
|
||||
rails %w(db:migrate db:schema:cache:dump db:rollback)
|
||||
|
||||
add_to_config <<-RUBY
|
||||
config.eager_load = true
|
||||
config.active_record.check_schema_cache_dump_version = false
|
||||
RUBY
|
||||
|
||||
switch_env("DATABASE_URL", "mysql2://127.0.0.1:1") do
|
||||
require "#{app_path}/config/environment"
|
||||
|
||||
assert ActiveRecord::Base.connection_pool.schema_cache.data_sources("posts")
|
||||
assert_raises ActiveRecord::ConnectionNotEstablished do
|
||||
ActiveRecord::Base.connection.execute("SELECT 1")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "active record establish_connection uses Rails.env if DATABASE_URL is not set" do
|
||||
require "#{app_path}/config/environment"
|
||||
orig_database_url = ENV.delete("DATABASE_URL")
|
||||
|
|
Loading…
Reference in a new issue