mirror of https://github.com/rails/rails
Remove legacy_connection_handling
This functionality has been deprecated since Rails 6.1 and can now be removed. I've deleted all code, docs, references, and tests related to this feature.
This commit is contained in:
parent
ca6d15ba17
commit
ad52c0a197
|
@ -1,3 +1,7 @@
|
|||
* Remove `ActiveRecord.legacy_connection_handling`.
|
||||
|
||||
*Eileen M. Uchitelle*
|
||||
|
||||
* `rails db:schema:{dump,load}` now checks `ENV["SCHEMA_FORMAT"]` before config
|
||||
|
||||
Since `rails db:structure:{dump,load}` was deprecated there wasn't a simple
|
||||
|
|
|
@ -178,9 +178,6 @@ module ActiveRecord
|
|||
singleton_class.attr_accessor :schema_cache_ignored_tables
|
||||
self.schema_cache_ignored_tables = []
|
||||
|
||||
singleton_class.attr_accessor :legacy_connection_handling
|
||||
self.legacy_connection_handling = true
|
||||
|
||||
singleton_class.attr_reader :default_timezone
|
||||
|
||||
# Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
|
||||
|
|
|
@ -11,7 +11,6 @@ module ActiveRecord
|
|||
autoload :Column
|
||||
autoload :PoolConfig
|
||||
autoload :PoolManager
|
||||
autoload :LegacyPoolManager
|
||||
autoload :SchemaCache
|
||||
autoload :Deduplicable
|
||||
|
||||
|
|
|
@ -88,28 +88,6 @@ module ActiveRecord
|
|||
ActiveSupport::IsolatedExecutionState[:active_record_prevent_writes] = prevent_writes
|
||||
end
|
||||
|
||||
# Prevent writing to the database regardless of role.
|
||||
#
|
||||
# In some cases you may want to prevent writes to the database
|
||||
# even if you are on a database that can write. +while_preventing_writes+
|
||||
# will prevent writes to the database for the duration of the block.
|
||||
#
|
||||
# This method does not provide the same protection as a readonly
|
||||
# user and is meant to be a safeguard against accidental writes.
|
||||
#
|
||||
# See +READ_QUERY+ for the queries that are blocked by this
|
||||
# method.
|
||||
def while_preventing_writes(enabled = true)
|
||||
unless ActiveRecord.legacy_connection_handling
|
||||
raise NotImplementedError, "`while_preventing_writes` is only available on the connection_handler with legacy_connection_handling"
|
||||
end
|
||||
|
||||
original, self.prevent_writes = self.prevent_writes, enabled
|
||||
yield
|
||||
ensure
|
||||
self.prevent_writes = original
|
||||
end
|
||||
|
||||
def connection_pool_names # :nodoc:
|
||||
owner_to_pool_manager.keys
|
||||
end
|
||||
|
@ -143,11 +121,7 @@ module ActiveRecord
|
|||
payload[:config] = db_config.configuration_hash
|
||||
end
|
||||
|
||||
if ActiveRecord.legacy_connection_handling
|
||||
owner_to_pool_manager[pool_config.connection_specification_name] ||= LegacyPoolManager.new
|
||||
else
|
||||
owner_to_pool_manager[pool_config.connection_specification_name] ||= PoolManager.new
|
||||
end
|
||||
owner_to_pool_manager[pool_config.connection_specification_name] ||= PoolManager.new
|
||||
pool_manager = get_pool_manager(pool_config.connection_specification_name)
|
||||
pool_manager.set_pool_config(role, shard, pool_config)
|
||||
|
||||
|
|
|
@ -151,16 +151,10 @@ module ActiveRecord
|
|||
|
||||
# Determines whether writes are currently being prevented.
|
||||
#
|
||||
# Returns true if the connection is a replica.
|
||||
#
|
||||
# If the application is using legacy handling, returns
|
||||
# true if +connection_handler.prevent_writes+ is set.
|
||||
#
|
||||
# If the application is using the new connection handling
|
||||
# will return true based on +current_preventing_writes+.
|
||||
# Returns true if the connection is a replica or returns
|
||||
# the value of +current_preventing_writes+.
|
||||
def preventing_writes?
|
||||
return true if replica?
|
||||
return ActiveRecord::Base.connection_handler.prevent_writes if ActiveRecord.legacy_connection_handling
|
||||
return false if connection_class.nil?
|
||||
|
||||
connection_class.current_preventing_writes
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ActiveRecord
|
||||
module ConnectionAdapters
|
||||
class LegacyPoolManager # :nodoc:
|
||||
def initialize
|
||||
@name_to_pool_config = {}
|
||||
end
|
||||
|
||||
def shard_names
|
||||
@name_to_pool_config.keys
|
||||
end
|
||||
|
||||
def pool_configs(_ = nil)
|
||||
@name_to_pool_config.values
|
||||
end
|
||||
|
||||
def remove_pool_config(_, shard)
|
||||
@name_to_pool_config.delete(shard)
|
||||
end
|
||||
|
||||
def get_pool_config(_, shard)
|
||||
@name_to_pool_config[shard]
|
||||
end
|
||||
|
||||
def set_pool_config(role, shard, pool_config)
|
||||
if pool_config
|
||||
@name_to_pool_config[shard] = pool_config
|
||||
else
|
||||
raise ArgumentError, "The `pool_config` for the :#{role} role and :#{shard} shard was `nil`. Please check your configuration. If you want your writing role to be something other than `:writing` set `config.active_record.writing_role` in your application configuration. The same setting should be applied for the `reading_role` if applicable."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -135,18 +135,12 @@ module ActiveRecord
|
|||
# Dog.first # finds first Dog record stored on the shard one replica
|
||||
# end
|
||||
def connected_to(role: nil, shard: nil, prevent_writes: false, &blk)
|
||||
if ActiveRecord.legacy_connection_handling
|
||||
if self != Base
|
||||
raise NotImplementedError, "`connected_to` can only be called on ActiveRecord::Base with legacy connection handling."
|
||||
end
|
||||
else
|
||||
if self != Base && !abstract_class
|
||||
raise NotImplementedError, "calling `connected_to` is only allowed on ActiveRecord::Base or abstract classes."
|
||||
end
|
||||
if self != Base && !abstract_class
|
||||
raise NotImplementedError, "calling `connected_to` is only allowed on ActiveRecord::Base or abstract classes."
|
||||
end
|
||||
|
||||
if name != connection_specification_name && !primary_class?
|
||||
raise NotImplementedError, "calling `connected_to` is only allowed on the abstract class that established the connection."
|
||||
end
|
||||
if name != connection_specification_name && !primary_class?
|
||||
raise NotImplementedError, "calling `connected_to` is only allowed on the abstract class that established the connection."
|
||||
end
|
||||
|
||||
unless role || shard
|
||||
|
@ -172,10 +166,6 @@ module ActiveRecord
|
|||
def connected_to_many(*classes, role:, shard: nil, prevent_writes: false)
|
||||
classes = classes.flatten
|
||||
|
||||
if ActiveRecord.legacy_connection_handling
|
||||
raise NotImplementedError, "connected_to_many is not available with legacy connection handling"
|
||||
end
|
||||
|
||||
if self != Base || classes.include?(Base)
|
||||
raise NotImplementedError, "connected_to_many can only be called on ActiveRecord::Base."
|
||||
end
|
||||
|
@ -196,10 +186,6 @@ module ActiveRecord
|
|||
# It is not recommended to use this method in a request since it
|
||||
# does not yield to a block like +connected_to+.
|
||||
def connecting_to(role: default_role, shard: default_shard, prevent_writes: false)
|
||||
if ActiveRecord.legacy_connection_handling
|
||||
raise NotImplementedError, "`connecting_to` is not available with `legacy_connection_handling`."
|
||||
end
|
||||
|
||||
prevent_writes = true if role == ActiveRecord.reading_role
|
||||
|
||||
append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
|
||||
|
@ -236,11 +222,7 @@ module ActiveRecord
|
|||
# See +READ_QUERY+ for the queries that are blocked by this
|
||||
# method.
|
||||
def while_preventing_writes(enabled = true, &block)
|
||||
if ActiveRecord.legacy_connection_handling
|
||||
connection_handler.while_preventing_writes(enabled, &block)
|
||||
else
|
||||
connected_to(role: current_role, prevent_writes: enabled, &block)
|
||||
end
|
||||
connected_to(role: current_role, prevent_writes: enabled, &block)
|
||||
end
|
||||
|
||||
# Returns true if role is the current connected role.
|
||||
|
@ -254,23 +236,12 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def lookup_connection_handler(handler_key) # :nodoc:
|
||||
if ActiveRecord.legacy_connection_handling
|
||||
handler_key ||= ActiveRecord.writing_role
|
||||
connection_handlers[handler_key] ||= ActiveRecord::ConnectionAdapters::ConnectionHandler.new
|
||||
else
|
||||
ActiveRecord::Base.connection_handler
|
||||
end
|
||||
ActiveRecord::Base.connection_handler
|
||||
end
|
||||
|
||||
# Clears the query cache for all connections associated with the current thread.
|
||||
def clear_query_caches_for_current_thread
|
||||
if ActiveRecord.legacy_connection_handling
|
||||
ActiveRecord::Base.connection_handlers.each_value do |handler|
|
||||
clear_on_handler(handler)
|
||||
end
|
||||
else
|
||||
clear_on_handler(ActiveRecord::Base.connection_handler)
|
||||
end
|
||||
clear_on_handler(ActiveRecord::Base.connection_handler)
|
||||
end
|
||||
|
||||
# Returns the connection currently associated with the class. This can
|
||||
|
@ -362,19 +333,10 @@ module ActiveRecord
|
|||
def with_role_and_shard(role, shard, prevent_writes)
|
||||
prevent_writes = true if role == ActiveRecord.reading_role
|
||||
|
||||
if ActiveRecord.legacy_connection_handling
|
||||
with_handler(role.to_sym) do
|
||||
connection_handler.while_preventing_writes(prevent_writes) do
|
||||
append_to_connected_to_stack(shard: shard, klasses: [self])
|
||||
yield
|
||||
end
|
||||
end
|
||||
else
|
||||
append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
|
||||
return_value = yield
|
||||
return_value.load if return_value.is_a? ActiveRecord::Relation
|
||||
return_value
|
||||
end
|
||||
append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
|
||||
return_value = yield
|
||||
return_value.load if return_value.is_a? ActiveRecord::Relation
|
||||
return_value
|
||||
ensure
|
||||
self.connected_to_stack.pop
|
||||
end
|
||||
|
|
|
@ -109,33 +109,6 @@ module ActiveRecord
|
|||
ActiveSupport::IsolatedExecutionState[:active_record_connection_handler] = handler
|
||||
end
|
||||
|
||||
def self.connection_handlers
|
||||
if ActiveRecord.legacy_connection_handling
|
||||
else
|
||||
raise NotImplementedError, "The new connection handling does not support accessing multiple connection handlers."
|
||||
end
|
||||
|
||||
@@connection_handlers ||= {}
|
||||
end
|
||||
|
||||
def self.connection_handlers=(handlers)
|
||||
if ActiveRecord.legacy_connection_handling
|
||||
ActiveSupport::Deprecation.warn(<<~MSG)
|
||||
Using legacy connection handling is deprecated. Please set
|
||||
`legacy_connection_handling` to `false` in your application.
|
||||
|
||||
The new connection handling does not support `connection_handlers`
|
||||
getter and setter.
|
||||
|
||||
Read more about how to migrate at: https://guides.rubyonrails.org/active_record_multiple_databases.html#migrate-to-the-new-connection-handling
|
||||
MSG
|
||||
else
|
||||
raise NotImplementedError, "The new connection handling does not support multiple connection handlers."
|
||||
end
|
||||
|
||||
@@connection_handlers = handlers
|
||||
end
|
||||
|
||||
def self.asynchronous_queries_session # :nodoc:
|
||||
asynchronous_queries_tracker.current_session
|
||||
end
|
||||
|
@ -155,16 +128,12 @@ module ActiveRecord
|
|||
# ActiveRecord::Base.current_role #=> :reading
|
||||
# end
|
||||
def self.current_role
|
||||
if ActiveRecord.legacy_connection_handling
|
||||
connection_handlers.key(connection_handler) || default_role
|
||||
else
|
||||
connected_to_stack.reverse_each do |hash|
|
||||
return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
|
||||
return hash[:role] if hash[:role] && hash[:klasses].include?(connection_class_for_self)
|
||||
end
|
||||
|
||||
default_role
|
||||
connected_to_stack.reverse_each do |hash|
|
||||
return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
|
||||
return hash[:role] if hash[:role] && hash[:klasses].include?(connection_class_for_self)
|
||||
end
|
||||
|
||||
default_role
|
||||
end
|
||||
|
||||
# Returns the symbol representing the current connected shard.
|
||||
|
@ -196,16 +165,12 @@ module ActiveRecord
|
|||
# ActiveRecord::Base.current_preventing_writes #=> false
|
||||
# end
|
||||
def self.current_preventing_writes
|
||||
if ActiveRecord.legacy_connection_handling
|
||||
connection_handler.prevent_writes
|
||||
else
|
||||
connected_to_stack.reverse_each do |hash|
|
||||
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
|
||||
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(connection_class_for_self)
|
||||
end
|
||||
|
||||
false
|
||||
connected_to_stack.reverse_each do |hash|
|
||||
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
|
||||
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(connection_class_for_self)
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def self.connected_to_stack # :nodoc:
|
||||
|
@ -325,7 +290,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
%w(
|
||||
reading_role writing_role legacy_connection_handling default_timezone index_nested_attribute_errors
|
||||
reading_role writing_role default_timezone index_nested_attribute_errors
|
||||
verbose_query_logs queues warn_on_records_fetched_greater_than maintain_test_schema
|
||||
application_record_class action_on_strict_loading_violation schema_format error_on_ignored_order
|
||||
timestamped_migrations dump_schema_after_migration dump_schemas suppress_multiple_database_warning
|
||||
|
|
|
@ -27,31 +27,15 @@ module ActiveRecord
|
|||
|
||||
def self.run
|
||||
pools = []
|
||||
|
||||
if ActiveRecord.legacy_connection_handling
|
||||
ActiveRecord::Base.connection_handlers.each do |key, handler|
|
||||
pools.concat(handler.connection_pool_list.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! })
|
||||
end
|
||||
else
|
||||
pools.concat(ActiveRecord::Base.connection_handler.all_connection_pools.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! })
|
||||
end
|
||||
|
||||
pools.concat(ActiveRecord::Base.connection_handler.all_connection_pools.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! })
|
||||
pools
|
||||
end
|
||||
|
||||
def self.complete(pools)
|
||||
pools.each { |pool| pool.disable_query_cache! }
|
||||
|
||||
if ActiveRecord.legacy_connection_handling
|
||||
ActiveRecord::Base.connection_handlers.each do |_, handler|
|
||||
handler.connection_pool_list.each do |pool|
|
||||
pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
|
||||
end
|
||||
end
|
||||
else
|
||||
ActiveRecord::Base.connection_handler.all_connection_pools.each do |pool|
|
||||
pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
|
||||
end
|
||||
ActiveRecord::Base.connection_handler.all_connection_pools.each do |pool|
|
||||
pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -270,9 +270,6 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|||
# and then establishes the connection.
|
||||
initializer "active_record.initialize_database" do
|
||||
ActiveSupport.on_load(:active_record) do
|
||||
if ActiveRecord.legacy_connection_handling
|
||||
self.connection_handlers = { ActiveRecord.writing_role => ActiveRecord::Base.default_connection_handler }
|
||||
end
|
||||
self.configurations = Rails.application.config.database_configuration
|
||||
|
||||
establish_connection
|
||||
|
|
|
@ -92,7 +92,6 @@ module ActiveRecord
|
|||
@fixture_connections = []
|
||||
@@already_loaded_fixtures ||= {}
|
||||
@connection_subscriber = nil
|
||||
@legacy_saved_pool_configs = Hash.new { |hash, key| hash[key] = {} }
|
||||
@saved_pool_configs = Hash.new { |hash, key| hash[key] = {} }
|
||||
|
||||
# Load fixtures once and begin transaction.
|
||||
|
@ -174,74 +173,38 @@ module ActiveRecord
|
|||
# need to share a connection pool so that the reading connection
|
||||
# can see data in the open transaction on the writing connection.
|
||||
def setup_shared_connection_pool
|
||||
if ActiveRecord.legacy_connection_handling
|
||||
writing_handler = ActiveRecord::Base.connection_handlers[ActiveRecord.writing_role]
|
||||
handler = ActiveRecord::Base.connection_handler
|
||||
|
||||
ActiveRecord::Base.connection_handlers.values.each do |handler|
|
||||
if handler != writing_handler
|
||||
handler.connection_pool_names.each do |name|
|
||||
writing_pool_manager = writing_handler.send(:owner_to_pool_manager)[name]
|
||||
return unless writing_pool_manager
|
||||
handler.connection_pool_names.each do |name|
|
||||
pool_manager = handler.send(:owner_to_pool_manager)[name]
|
||||
pool_manager.shard_names.each do |shard_name|
|
||||
writing_pool_config = pool_manager.get_pool_config(ActiveRecord.writing_role, shard_name)
|
||||
@saved_pool_configs[name][shard_name] ||= {}
|
||||
pool_manager.role_names.each do |role|
|
||||
next unless pool_config = pool_manager.get_pool_config(role, shard_name)
|
||||
next if pool_config == writing_pool_config
|
||||
|
||||
pool_manager = handler.send(:owner_to_pool_manager)[name]
|
||||
@legacy_saved_pool_configs[handler][name] ||= {}
|
||||
pool_manager.shard_names.each do |shard_name|
|
||||
writing_pool_config = writing_pool_manager.get_pool_config(nil, shard_name)
|
||||
pool_config = pool_manager.get_pool_config(nil, shard_name)
|
||||
next if pool_config == writing_pool_config
|
||||
|
||||
@legacy_saved_pool_configs[handler][name][shard_name] = pool_config
|
||||
pool_manager.set_pool_config(nil, shard_name, writing_pool_config)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
handler = ActiveRecord::Base.connection_handler
|
||||
|
||||
handler.connection_pool_names.each do |name|
|
||||
pool_manager = handler.send(:owner_to_pool_manager)[name]
|
||||
pool_manager.shard_names.each do |shard_name|
|
||||
writing_pool_config = pool_manager.get_pool_config(ActiveRecord.writing_role, shard_name)
|
||||
@saved_pool_configs[name][shard_name] ||= {}
|
||||
pool_manager.role_names.each do |role|
|
||||
next unless pool_config = pool_manager.get_pool_config(role, shard_name)
|
||||
next if pool_config == writing_pool_config
|
||||
|
||||
@saved_pool_configs[name][shard_name][role] = pool_config
|
||||
pool_manager.set_pool_config(role, shard_name, writing_pool_config)
|
||||
end
|
||||
@saved_pool_configs[name][shard_name][role] = pool_config
|
||||
pool_manager.set_pool_config(role, shard_name, writing_pool_config)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def teardown_shared_connection_pool
|
||||
if ActiveRecord.legacy_connection_handling
|
||||
@legacy_saved_pool_configs.each_pair do |handler, names|
|
||||
names.each_pair do |name, shards|
|
||||
shards.each_pair do |shard_name, pool_config|
|
||||
pool_manager = handler.send(:owner_to_pool_manager)[name]
|
||||
pool_manager.set_pool_config(nil, shard_name, pool_config)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
handler = ActiveRecord::Base.connection_handler
|
||||
handler = ActiveRecord::Base.connection_handler
|
||||
|
||||
@saved_pool_configs.each_pair do |name, shards|
|
||||
pool_manager = handler.send(:owner_to_pool_manager)[name]
|
||||
shards.each_pair do |shard_name, roles|
|
||||
roles.each_pair do |role, pool_config|
|
||||
next unless pool_manager.get_pool_config(role, shard_name)
|
||||
@saved_pool_configs.each_pair do |name, shards|
|
||||
pool_manager = handler.send(:owner_to_pool_manager)[name]
|
||||
shards.each_pair do |shard_name, roles|
|
||||
roles.each_pair do |role, pool_config|
|
||||
next unless pool_manager.get_pool_config(role, shard_name)
|
||||
|
||||
pool_manager.set_pool_config(role, shard_name, pool_config)
|
||||
end
|
||||
pool_manager.set_pool_config(role, shard_name, pool_config)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@legacy_saved_pool_configs.clear
|
||||
@saved_pool_configs.clear
|
||||
end
|
||||
|
||||
|
|
|
@ -143,130 +143,4 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
class AdapterPreventWritesLegacyTest < ActiveRecord::TestCase
|
||||
def setup
|
||||
@old_value = ActiveRecord.legacy_connection_handling
|
||||
ActiveRecord.legacy_connection_handling = true
|
||||
|
||||
@connection = ActiveRecord::Base.connection
|
||||
@connection_handler = ActiveRecord::Base.connection_handler
|
||||
end
|
||||
|
||||
def teardown
|
||||
clean_up_legacy_connection_handlers
|
||||
ActiveRecord.legacy_connection_handling = @old_value
|
||||
end
|
||||
|
||||
def test_preventing_writes_predicate_legacy
|
||||
assert_not_predicate @connection, :preventing_writes?
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_predicate @connection, :preventing_writes?
|
||||
end
|
||||
|
||||
assert_not_predicate @connection, :preventing_writes?
|
||||
end
|
||||
|
||||
def test_errors_when_an_insert_query_is_called_while_preventing_writes
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_raises(ActiveRecord::ReadOnlyError) do
|
||||
@connection.insert("INSERT INTO subscribers(nick) VALUES ('138853948594')", nil, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_errors_when_an_update_query_is_called_while_preventing_writes
|
||||
@connection.insert("INSERT INTO subscribers(nick) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_raises(ActiveRecord::ReadOnlyError) do
|
||||
@connection.update("UPDATE subscribers SET nick = '9989' WHERE nick = '138853948594'")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_errors_when_a_delete_query_is_called_while_preventing_writes
|
||||
@connection.insert("INSERT INTO subscribers(nick) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_raises(ActiveRecord::ReadOnlyError) do
|
||||
@connection.delete("DELETE FROM subscribers WHERE nick = '138853948594'")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes
|
||||
@connection.insert("INSERT INTO subscribers(nick) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
result = @connection.select_all("SELECT subscribers.* FROM subscribers WHERE nick = '138853948594'")
|
||||
assert_equal 1, result.length
|
||||
end
|
||||
end
|
||||
|
||||
if ActiveRecord::Base.connection.supports_common_table_expressions?
|
||||
def test_doesnt_error_when_a_read_query_with_a_cte_is_called_while_preventing_writes
|
||||
@connection.insert("INSERT INTO subscribers(nick) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
result = @connection.select_all(<<~SQL)
|
||||
WITH matching_subscribers AS (SELECT subscribers.* FROM subscribers WHERE nick = '138853948594')
|
||||
SELECT * FROM matching_subscribers
|
||||
SQL
|
||||
assert_equal 1, result.length
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_doesnt_error_when_a_select_query_starting_with_a_slash_star_comment_is_called_while_preventing_writes
|
||||
@connection.insert("INSERT INTO subscribers(nick) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
result = @connection.select_all("/* some comment */ SELECT subscribers.* FROM subscribers WHERE nick = '138853948594'")
|
||||
assert_equal 1, result.length
|
||||
end
|
||||
end
|
||||
|
||||
def test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_raises(ActiveRecord::ReadOnlyError) do
|
||||
@connection.insert("/* some comment */ INSERT INTO subscribers(nick) VALUES ('138853948594')", nil, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_doesnt_error_when_a_select_query_starting_with_double_dash_comments_is_called_while_preventing_writes
|
||||
@connection.insert("INSERT INTO subscribers(nick) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
result = @connection.select_all("-- some comment\n-- comment about INSERT\nSELECT subscribers.* FROM subscribers WHERE nick = '138853948594'")
|
||||
assert_equal 1, result.length
|
||||
end
|
||||
end
|
||||
|
||||
def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_raises(ActiveRecord::ReadOnlyError) do
|
||||
@connection.insert("-- some comment\nINSERT INTO subscribers(nick) VALUES ('138853948594')", nil, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_containing_read_command_is_called_while_preventing_writes
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_raises(ActiveRecord::ReadOnlyError) do
|
||||
@connection.insert("/* SELECT */ INSERT INTO subscribers(nick) VALUES ('138853948594')", nil, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_raises(ActiveRecord::ReadOnlyError) do
|
||||
@connection.insert("-- SELECT\nINSERT INTO subscribers(nick) VALUES ('138853948594')", nil, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -100,109 +100,3 @@ class Mysql2AdapterPreventWritesTest < ActiveRecord::Mysql2TestCase
|
|||
super(@conn, "ex", definition, &block)
|
||||
end
|
||||
end
|
||||
|
||||
class Mysql2AdapterPreventWritesLegacyTest < ActiveRecord::Mysql2TestCase
|
||||
include DdlHelper
|
||||
|
||||
def setup
|
||||
@old_value = ActiveRecord.legacy_connection_handling
|
||||
ActiveRecord.legacy_connection_handling = true
|
||||
|
||||
@conn = ActiveRecord::Base.connection
|
||||
@connection_handler = ActiveRecord::Base.connection_handler
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveRecord.legacy_connection_handling = @old_value
|
||||
end
|
||||
|
||||
def test_errors_when_an_insert_query_is_called_while_preventing_writes
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_raises(ActiveRecord::ReadOnlyError) do
|
||||
@conn.insert("INSERT INTO `engines` (`car_id`) VALUES ('138853948594')")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_errors_when_an_update_query_is_called_while_preventing_writes
|
||||
@conn.insert("INSERT INTO `engines` (`car_id`) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_raises(ActiveRecord::ReadOnlyError) do
|
||||
@conn.update("UPDATE `engines` SET `engines`.`car_id` = '9989' WHERE `engines`.`car_id` = '138853948594'")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_errors_when_a_delete_query_is_called_while_preventing_writes
|
||||
@conn.execute("INSERT INTO `engines` (`car_id`) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_raises(ActiveRecord::ReadOnlyError) do
|
||||
@conn.execute("DELETE FROM `engines` where `engines`.`car_id` = '138853948594'")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_errors_when_a_replace_query_is_called_while_preventing_writes
|
||||
@conn.execute("INSERT INTO `engines` (`car_id`) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_raises(ActiveRecord::ReadOnlyError) do
|
||||
@conn.execute("REPLACE INTO `engines` SET `engines`.`car_id` = '249823948'")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes
|
||||
@conn.execute("INSERT INTO `engines` (`car_id`) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_equal 1, @conn.execute("SELECT `engines`.* FROM `engines` WHERE `engines`.`car_id` = '138853948594'").entries.count
|
||||
end
|
||||
end
|
||||
|
||||
def test_doesnt_error_when_a_show_query_is_called_while_preventing_writes
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_equal 2, @conn.execute("SHOW FULL FIELDS FROM `engines`").entries.count
|
||||
end
|
||||
end
|
||||
|
||||
def test_doesnt_error_when_a_set_query_is_called_while_preventing_writes
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_nil @conn.execute("SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci")
|
||||
end
|
||||
end
|
||||
|
||||
def test_doesnt_error_when_a_describe_query_is_called_while_preventing_writes
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_equal 2, @conn.execute("DESCRIBE engines").entries.count
|
||||
end
|
||||
end
|
||||
|
||||
def test_doesnt_error_when_a_desc_query_is_called_while_preventing_writes
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_equal 2, @conn.execute("DESC engines").entries.count
|
||||
end
|
||||
end
|
||||
|
||||
def test_doesnt_error_when_a_read_query_with_leading_chars_is_called_while_preventing_writes
|
||||
@conn.execute("INSERT INTO `engines` (`car_id`) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_equal 1, @conn.execute("/*action:index*/(\n( SELECT `engines`.* FROM `engines` WHERE `engines`.`car_id` = '138853948594' ) )").entries.count
|
||||
end
|
||||
end
|
||||
|
||||
def test_doesnt_error_when_a_use_query_is_called_while_preventing_writes
|
||||
@connection_handler.while_preventing_writes do
|
||||
db_name = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary").database
|
||||
assert_nil @conn.execute("USE #{db_name}")
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def with_example_table(definition = "id int auto_increment primary key, number int, data varchar(255)", &block)
|
||||
super(@conn, "ex", definition, &block)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -99,107 +99,5 @@ module ActiveRecord
|
|||
super(@connection, "ex", definition, &block)
|
||||
end
|
||||
end
|
||||
|
||||
class PostgreSQLAdapterPreventWritesLegacyTest < ActiveRecord::PostgreSQLTestCase
|
||||
self.use_transactional_tests = false
|
||||
include DdlHelper
|
||||
include ConnectionHelper
|
||||
|
||||
def setup
|
||||
@old_value = ActiveRecord.legacy_connection_handling
|
||||
ActiveRecord.legacy_connection_handling = true
|
||||
|
||||
@connection = ActiveRecord::Base.connection
|
||||
@connection_handler = ActiveRecord::Base.connection_handler
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveRecord.legacy_connection_handling = @old_value
|
||||
end
|
||||
|
||||
def test_errors_when_an_insert_query_is_called_while_preventing_writes
|
||||
with_example_table do
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_raises(ActiveRecord::ReadOnlyError) do
|
||||
@connection.execute("INSERT INTO ex (data) VALUES ('138853948594')")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_errors_when_an_update_query_is_called_while_preventing_writes
|
||||
with_example_table do
|
||||
@connection.execute("INSERT INTO ex (data) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_raises(ActiveRecord::ReadOnlyError) do
|
||||
@connection.execute("UPDATE ex SET data = '9989' WHERE data = '138853948594'")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_errors_when_a_delete_query_is_called_while_preventing_writes
|
||||
with_example_table do
|
||||
@connection.execute("INSERT INTO ex (data) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_raises(ActiveRecord::ReadOnlyError) do
|
||||
@connection.execute("DELETE FROM ex where data = '138853948594'")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes
|
||||
with_example_table do
|
||||
@connection.execute("INSERT INTO ex (data) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_equal 1, @connection.execute("SELECT * FROM ex WHERE data = '138853948594'").entries.count
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_doesnt_error_when_a_show_query_is_called_while_preventing_writes
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_equal 1, @connection.execute("SHOW TIME ZONE").entries.count
|
||||
end
|
||||
end
|
||||
|
||||
def test_doesnt_error_when_a_set_query_is_called_while_preventing_writes
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_equal [], @connection.execute("SET standard_conforming_strings = on").entries
|
||||
end
|
||||
end
|
||||
|
||||
def test_doesnt_error_when_a_read_query_with_leading_chars_is_called_while_preventing_writes
|
||||
with_example_table do
|
||||
@connection.execute("INSERT INTO ex (data) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_equal 1, @connection.execute("/*action:index*/(\n( SELECT * FROM ex WHERE data = '138853948594' ) )").entries.count
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_doesnt_error_when_a_read_query_with_cursors_is_called_while_preventing_writes
|
||||
with_example_table do
|
||||
@connection_handler.while_preventing_writes do
|
||||
@connection.transaction do
|
||||
assert_equal [], @connection.execute("DECLARE cur_ex CURSOR FOR SELECT * FROM ex").entries
|
||||
assert_equal [], @connection.execute("FETCH cur_ex").entries
|
||||
assert_equal [], @connection.execute("MOVE cur_ex").entries
|
||||
assert_equal [], @connection.execute("CLOSE cur_ex").entries
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def with_example_table(definition = "id serial primary key, number integer, data character varying(255)", &block)
|
||||
super(@connection, "ex", definition, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -89,98 +89,5 @@ module ActiveRecord
|
|||
super(@conn, table_name, definition, &block)
|
||||
end
|
||||
end
|
||||
|
||||
class SQLite3AdapterPreventWritesLegacyTest < ActiveRecord::SQLite3TestCase
|
||||
include DdlHelper
|
||||
self.use_transactional_tests = false
|
||||
|
||||
def setup
|
||||
@old_value = ActiveRecord.legacy_connection_handling
|
||||
ActiveRecord.legacy_connection_handling = true
|
||||
|
||||
@conn = ActiveRecord::Base.connection
|
||||
|
||||
@connection_handler = ActiveRecord::Base.connection_handler
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveRecord.legacy_connection_handling = @old_value
|
||||
end
|
||||
|
||||
def test_errors_when_an_insert_query_is_called_while_preventing_writes
|
||||
with_example_table "id int, data string" do
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_raises(ActiveRecord::ReadOnlyError) do
|
||||
@conn.execute("INSERT INTO ex (data) VALUES ('138853948594')")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_errors_when_an_update_query_is_called_while_preventing_writes
|
||||
with_example_table "id int, data string" do
|
||||
@conn.execute("INSERT INTO ex (data) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_raises(ActiveRecord::ReadOnlyError) do
|
||||
@conn.execute("UPDATE ex SET data = '9989' WHERE data = '138853948594'")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_errors_when_a_delete_query_is_called_while_preventing_writes
|
||||
with_example_table "id int, data string" do
|
||||
@conn.execute("INSERT INTO ex (data) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_raises(ActiveRecord::ReadOnlyError) do
|
||||
@conn.execute("DELETE FROM ex where data = '138853948594'")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_errors_when_a_replace_query_is_called_while_preventing_writes
|
||||
with_example_table "id int, data string" do
|
||||
@conn.execute("INSERT INTO ex (data) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_raises(ActiveRecord::ReadOnlyError) do
|
||||
@conn.execute("REPLACE INTO ex (data) VALUES ('249823948')")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes
|
||||
with_example_table "id int, data string" do
|
||||
@conn.execute("INSERT INTO ex (data) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_equal 1, @conn.execute("SELECT data from ex WHERE data = '138853948594'").count
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_doesnt_error_when_a_read_query_with_leading_chars_is_called_while_preventing_writes
|
||||
with_example_table "id int, data string" do
|
||||
@conn.execute("INSERT INTO ex (data) VALUES ('138853948594')")
|
||||
|
||||
@connection_handler.while_preventing_writes do
|
||||
assert_equal 1, @conn.execute("/*action:index*/ SELECT data from ex WHERE data = '138853948594'").count
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def with_example_table(definition = nil, table_name = "ex", &block)
|
||||
definition ||= <<~SQL
|
||||
id integer PRIMARY KEY AUTOINCREMENT,
|
||||
number integer
|
||||
SQL
|
||||
super(@conn, table_name, definition, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -93,136 +93,4 @@ class BasePreventWritesTest < ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
class BasePreventWritesLegacyTest < ActiveRecord::TestCase
|
||||
def setup
|
||||
@old_value = ActiveRecord.legacy_connection_handling
|
||||
ActiveRecord.legacy_connection_handling = true
|
||||
ActiveRecord::Base.establish_connection :arunit
|
||||
ARUnit2Model.establish_connection :arunit2
|
||||
end
|
||||
|
||||
def teardown
|
||||
clean_up_legacy_connection_handlers
|
||||
ActiveRecord.legacy_connection_handling = @old_value
|
||||
end
|
||||
|
||||
if !in_memory_db?
|
||||
test "creating a record raises if preventing writes" do
|
||||
ActiveRecord::Base.connection_handler.while_preventing_writes do
|
||||
error = assert_raises ActiveRecord::ReadOnlyError do
|
||||
Bird.create! name: "Bluejay"
|
||||
end
|
||||
|
||||
assert_match %r/\AWrite query attempted while in readonly mode: INSERT /, error.message
|
||||
end
|
||||
end
|
||||
|
||||
test "updating a record raises if preventing writes" do
|
||||
bird = Bird.create! name: "Bluejay"
|
||||
|
||||
ActiveRecord::Base.connection_handler.while_preventing_writes do
|
||||
error = assert_raises ActiveRecord::ReadOnlyError do
|
||||
bird.update! name: "Robin"
|
||||
end
|
||||
|
||||
assert_match %r/\AWrite query attempted while in readonly mode: UPDATE /, error.message
|
||||
end
|
||||
end
|
||||
|
||||
test "deleting a record raises if preventing writes" do
|
||||
bird = Bird.create! name: "Bluejay"
|
||||
|
||||
ActiveRecord::Base.connection_handler.while_preventing_writes do
|
||||
error = assert_raises ActiveRecord::ReadOnlyError do
|
||||
bird.destroy!
|
||||
end
|
||||
|
||||
assert_match %r/\AWrite query attempted while in readonly mode: DELETE /, error.message
|
||||
end
|
||||
end
|
||||
|
||||
test "selecting a record does not raise if preventing writes" do
|
||||
bird = Bird.create! name: "Bluejay"
|
||||
|
||||
ActiveRecord::Base.connection_handler.while_preventing_writes do
|
||||
assert_equal bird, Bird.where(name: "Bluejay").last
|
||||
end
|
||||
end
|
||||
|
||||
test "an explain query does not raise if preventing writes" do
|
||||
Bird.create!(name: "Bluejay")
|
||||
|
||||
ActiveRecord::Base.connection_handler.while_preventing_writes do
|
||||
assert_queries(2) { Bird.where(name: "Bluejay").explain }
|
||||
end
|
||||
end
|
||||
|
||||
test "an empty transaction does not raise if preventing writes" do
|
||||
ActiveRecord::Base.connection_handler.while_preventing_writes do
|
||||
assert_queries(2, ignore_none: true) do
|
||||
Bird.transaction do
|
||||
ActiveRecord::Base.connection.materialize_transactions
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "preventing writes applies to all connections on a handler" do
|
||||
ActiveRecord::Base.connection_handler.while_preventing_writes do
|
||||
conn1_error = assert_raises ActiveRecord::ReadOnlyError do
|
||||
assert_equal ActiveRecord::Base.connection, Bird.connection
|
||||
assert_not_equal ARUnit2Model.connection, Bird.connection
|
||||
Bird.create!(name: "Bluejay")
|
||||
end
|
||||
|
||||
assert_match %r/\AWrite query attempted while in readonly mode: INSERT /, conn1_error.message
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connection_handler.while_preventing_writes do
|
||||
conn2_error = assert_raises ActiveRecord::ReadOnlyError do
|
||||
assert_not_equal ActiveRecord::Base.connection, Professor.connection
|
||||
assert_equal ARUnit2Model.connection, Professor.connection
|
||||
Professor.create!(name: "Professor Bluejay")
|
||||
end
|
||||
|
||||
assert_match %r/\AWrite query attempted while in readonly mode: INSERT /, conn2_error.message
|
||||
end
|
||||
end
|
||||
|
||||
test "preventing writes with multiple handlers" do
|
||||
ActiveRecord::Base.connects_to(database: { writing: :arunit, reading: :arunit })
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :writing) do
|
||||
conn1_error = assert_raises ActiveRecord::ReadOnlyError do
|
||||
assert_equal :writing, ActiveRecord::Base.current_role
|
||||
|
||||
ActiveRecord::Base.connection_handler.while_preventing_writes do
|
||||
Bird.create!(name: "Bluejay")
|
||||
end
|
||||
end
|
||||
|
||||
assert_match %r/\AWrite query attempted while in readonly mode: INSERT /, conn1_error.message
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :reading) do
|
||||
conn2_error = assert_raises ActiveRecord::ReadOnlyError do
|
||||
assert_equal :reading, ActiveRecord::Base.current_role
|
||||
|
||||
ActiveRecord::Base.connection_handler.while_preventing_writes do
|
||||
Bird.create!(name: "Bluejay")
|
||||
end
|
||||
end
|
||||
|
||||
assert_match %r/\AWrite query attempted while in readonly mode: INSERT /, conn2_error.message
|
||||
end
|
||||
end
|
||||
|
||||
test "current_preventing_writes" do
|
||||
ActiveRecord::Base.connection_handler.while_preventing_writes do
|
||||
assert ActiveRecord::Base.current_preventing_writes, "expected connection current_preventing_writes to return true"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1665,20 +1665,6 @@ class BasicsTest < ActiveRecord::TestCase
|
|||
assert_equal "`connects_to` can only be called on ActiveRecord::Base or abstract classes", error.message
|
||||
end
|
||||
|
||||
test "cannot call connected_to on subclasses of ActiveRecord::Base with legacy connection handling" do
|
||||
old_value = ActiveRecord.legacy_connection_handling
|
||||
ActiveRecord.legacy_connection_handling = true
|
||||
|
||||
error = assert_raises(NotImplementedError) do
|
||||
Bird.connected_to(role: :reading) { }
|
||||
end
|
||||
|
||||
assert_equal "`connected_to` can only be called on ActiveRecord::Base with legacy connection handling.", error.message
|
||||
ensure
|
||||
clean_up_legacy_connection_handlers
|
||||
ActiveRecord.legacy_connection_handling = old_value
|
||||
end
|
||||
|
||||
test "cannot call connected_to with role and shard on non-abstract classes" do
|
||||
error = assert_raises(NotImplementedError) do
|
||||
Bird.connected_to(role: :reading, shard: :default) { }
|
||||
|
@ -1727,28 +1713,6 @@ class BasicsTest < ActiveRecord::TestCase
|
|||
ActiveRecord::Base.connected_to_stack.pop
|
||||
end
|
||||
|
||||
test "#connecting_to doesn't work with legacy connection handling" do
|
||||
old_value = ActiveRecord.legacy_connection_handling
|
||||
ActiveRecord.legacy_connection_handling = true
|
||||
|
||||
assert_raises NotImplementedError do
|
||||
SecondAbstractClass.connecting_to(role: :writing, prevent_writes: true)
|
||||
end
|
||||
ensure
|
||||
ActiveRecord.legacy_connection_handling = old_value
|
||||
end
|
||||
|
||||
test "#connected_to_many doesn't work with legacy connection handling" do
|
||||
old_value = ActiveRecord.legacy_connection_handling
|
||||
ActiveRecord.legacy_connection_handling = true
|
||||
|
||||
assert_raises NotImplementedError do
|
||||
ActiveRecord::Base.connected_to_many([SecondAbstractClass], role: :writing)
|
||||
end
|
||||
ensure
|
||||
ActiveRecord.legacy_connection_handling = old_value
|
||||
end
|
||||
|
||||
test "#connected_to_many cannot be called on anything but ActiveRecord::Base" do
|
||||
assert_raises NotImplementedError do
|
||||
SecondAbstractClass.connected_to_many([SecondAbstractClass], role: :writing)
|
||||
|
|
|
@ -1,479 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "cases/helper"
|
||||
require "models/person"
|
||||
|
||||
module ActiveRecord
|
||||
module ConnectionAdapters
|
||||
class LegacyConnectionHandlersMultiDbTest < ActiveRecord::TestCase
|
||||
self.use_transactional_tests = false
|
||||
|
||||
fixtures :people
|
||||
|
||||
def setup
|
||||
@old_value = ActiveRecord.legacy_connection_handling
|
||||
ActiveRecord.legacy_connection_handling = true
|
||||
assert_deprecated do
|
||||
ActiveRecord::Base.connection_handlers = { writing: ActiveRecord::Base.default_connection_handler }
|
||||
end
|
||||
|
||||
@handlers = { writing: ConnectionHandler.new, reading: ConnectionHandler.new }
|
||||
@rw_handler = @handlers[:writing]
|
||||
@ro_handler = @handlers[:reading]
|
||||
@owner_name = "ActiveRecord::Base"
|
||||
db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
|
||||
|
||||
@rw_pool = @handlers[:writing].establish_connection(db_config)
|
||||
@ro_pool = @handlers[:reading].establish_connection(db_config)
|
||||
end
|
||||
|
||||
def teardown
|
||||
clean_up_legacy_connection_handlers
|
||||
ActiveRecord.legacy_connection_handling = @old_value
|
||||
end
|
||||
|
||||
class SecondaryBase < ActiveRecord::Base
|
||||
self.abstract_class = true
|
||||
end
|
||||
|
||||
class MultiConnectionTestModel < SecondaryBase
|
||||
end
|
||||
|
||||
def test_multiple_connection_handlers_works_in_a_threaded_environment
|
||||
tf_writing = Tempfile.open "test_writing"
|
||||
tf_reading = Tempfile.open "test_reading"
|
||||
|
||||
# We need to use a role for reading not named reading, otherwise we'll prevent writes
|
||||
# and won't be able to write to the second connection.
|
||||
SecondaryBase.connects_to database: { writing: { database: tf_writing.path, adapter: "sqlite3" }, secondary: { database: tf_reading.path, adapter: "sqlite3" } }
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :writing) do
|
||||
MultiConnectionTestModel.connection.execute("CREATE TABLE `multi_connection_test_models` (connection_role VARCHAR (255))")
|
||||
MultiConnectionTestModel.connection.execute("INSERT INTO multi_connection_test_models VALUES ('writing')")
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :secondary) do
|
||||
MultiConnectionTestModel.connection.execute("CREATE TABLE `multi_connection_test_models` (connection_role VARCHAR (255))")
|
||||
MultiConnectionTestModel.connection.execute("INSERT INTO multi_connection_test_models VALUES ('reading')")
|
||||
end
|
||||
|
||||
read_latch = Concurrent::CountDownLatch.new
|
||||
write_latch = Concurrent::CountDownLatch.new
|
||||
|
||||
MultiConnectionTestModel.connection
|
||||
|
||||
thread = Thread.new do
|
||||
MultiConnectionTestModel.connection
|
||||
|
||||
write_latch.wait
|
||||
assert_equal "writing", MultiConnectionTestModel.connection.select_value("SELECT connection_role from multi_connection_test_models")
|
||||
read_latch.count_down
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :secondary) do
|
||||
write_latch.count_down
|
||||
assert_equal "reading", MultiConnectionTestModel.connection.select_value("SELECT connection_role from multi_connection_test_models")
|
||||
read_latch.wait
|
||||
end
|
||||
|
||||
thread.join
|
||||
ensure
|
||||
tf_reading.close
|
||||
tf_reading.unlink
|
||||
tf_writing.close
|
||||
tf_writing.unlink
|
||||
end
|
||||
|
||||
def test_loading_relations_with_multi_db_connection_handlers
|
||||
# We need to use a role for reading not named reading, otherwise we'll prevent writes
|
||||
# and won't be able to write to the second connection.
|
||||
SecondaryBase.connects_to database: { writing: { database: ":memory:", adapter: "sqlite3" }, secondary: { database: ":memory:", adapter: "sqlite3" } }
|
||||
|
||||
relation = ActiveRecord::Base.connected_to(role: :secondary) do
|
||||
MultiConnectionTestModel.connection.execute("CREATE TABLE `multi_connection_test_models` (connection_role VARCHAR (255))")
|
||||
MultiConnectionTestModel.create!(connection_role: "reading")
|
||||
MultiConnectionTestModel.where(connection_role: "reading")
|
||||
end
|
||||
|
||||
assert_equal "reading", relation.first.connection_role
|
||||
end
|
||||
|
||||
unless in_memory_db?
|
||||
def test_not_setting_writing_role_while_using_another_named_role_raises
|
||||
old_handler = ActiveRecord::Base.connection_handler
|
||||
assert_deprecated do
|
||||
ActiveRecord::Base.connection_handlers = { writing: ConnectionHandler.new }
|
||||
end
|
||||
ActiveRecord::Base.connection_handler = ActiveRecord::Base.connection_handlers[:writing]
|
||||
ActiveRecord::Base.establish_connection :arunit
|
||||
|
||||
ActiveRecord::Base.connects_to(shards: { default: { all: :arunit }, one: { all: :arunit } })
|
||||
|
||||
assert_raises(ArgumentError) { setup_shared_connection_pool }
|
||||
ensure
|
||||
ActiveRecord::Base.connection_handler = old_handler
|
||||
ActiveRecord::Base.establish_connection :arunit
|
||||
end
|
||||
|
||||
def test_setting_writing_role_while_using_another_named_role_does_not_raise
|
||||
old_role, ActiveRecord.writing_role = ActiveRecord.writing_role, :all
|
||||
old_handler = ActiveRecord::Base.connection_handler
|
||||
assert_deprecated do
|
||||
ActiveRecord::Base.connection_handlers = { all: ConnectionHandler.new }
|
||||
end
|
||||
ActiveRecord::Base.connection_handler = ActiveRecord::Base.connection_handlers[:all]
|
||||
ActiveRecord::Base.establish_connection :arunit
|
||||
|
||||
ActiveRecord::Base.connects_to(shards: { default: { all: :arunit }, one: { all: :arunit } })
|
||||
|
||||
assert_nothing_raised { setup_shared_connection_pool }
|
||||
ensure
|
||||
ActiveRecord.writing_role = old_role
|
||||
ActiveRecord::Base.connection_handler = old_handler
|
||||
ActiveRecord::Base.establish_connection :arunit
|
||||
end
|
||||
|
||||
def test_establish_connection_using_3_levels_config
|
||||
previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
|
||||
|
||||
config = {
|
||||
"default_env" => {
|
||||
"readonly" => { "adapter" => "sqlite3", "database" => "test/db/readonly.sqlite3", "replica" => true },
|
||||
"default" => { "adapter" => "sqlite3", "database" => "test/db/primary.sqlite3" }
|
||||
}
|
||||
}
|
||||
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
|
||||
|
||||
ActiveRecord::Base.connects_to(database: { writing: :default, reading: :readonly })
|
||||
|
||||
@pool = ActiveRecord::Base.connection_handlers[:writing].retrieve_connection_pool("ActiveRecord::Base")
|
||||
assert_not_nil @pool
|
||||
assert_equal "test/db/primary.sqlite3", @pool.db_config.database
|
||||
assert_equal "default", @pool.db_config.name
|
||||
|
||||
@pool2 = ActiveRecord::Base.connection_handlers[:reading].retrieve_connection_pool("ActiveRecord::Base")
|
||||
assert_not_nil @pool2
|
||||
assert_equal "test/db/readonly.sqlite3", @pool2.db_config.database
|
||||
assert_equal "readonly", @pool2.db_config.name
|
||||
ensure
|
||||
ActiveRecord::Base.configurations = @prev_configs
|
||||
ActiveRecord::Base.establish_connection(:arunit)
|
||||
ENV["RAILS_ENV"] = previous_env
|
||||
end
|
||||
|
||||
def test_switching_connections_via_handler
|
||||
previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
|
||||
|
||||
config = {
|
||||
"default_env" => {
|
||||
"readonly" => { "adapter" => "sqlite3", "database" => "test/db/readonly.sqlite3" },
|
||||
"primary" => { "adapter" => "sqlite3", "database" => "test/db/primary.sqlite3" }
|
||||
}
|
||||
}
|
||||
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
|
||||
|
||||
ActiveRecord::Base.connects_to(database: { writing: :primary, reading: :readonly })
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :reading) do
|
||||
@ro_handler = ActiveRecord::Base.connection_handler
|
||||
assert_equal ActiveRecord::Base.connection_handler, ActiveRecord::Base.connection_handlers[:reading]
|
||||
assert_equal :reading, ActiveRecord::Base.current_role
|
||||
assert ActiveRecord::Base.connected_to?(role: :reading)
|
||||
assert_not ActiveRecord::Base.connected_to?(role: :writing)
|
||||
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :writing) do
|
||||
assert_equal ActiveRecord::Base.connection_handler, ActiveRecord::Base.connection_handlers[:writing]
|
||||
assert_not_equal @ro_handler, ActiveRecord::Base.connection_handler
|
||||
assert_equal :writing, ActiveRecord::Base.current_role
|
||||
assert ActiveRecord::Base.connected_to?(role: :writing)
|
||||
assert_not ActiveRecord::Base.connected_to?(role: :reading)
|
||||
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
|
||||
end
|
||||
ensure
|
||||
ActiveRecord::Base.configurations = @prev_configs
|
||||
ActiveRecord::Base.establish_connection(:arunit)
|
||||
ENV["RAILS_ENV"] = previous_env
|
||||
end
|
||||
|
||||
def test_establish_connection_using_3_levels_config_with_non_default_handlers
|
||||
previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
|
||||
|
||||
config = {
|
||||
"default_env" => {
|
||||
"readonly" => { "adapter" => "sqlite3", "database" => "test/db/readonly.sqlite3" },
|
||||
"primary" => { "adapter" => "sqlite3", "database" => "test/db/primary.sqlite3" }
|
||||
}
|
||||
}
|
||||
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
|
||||
|
||||
ActiveRecord::Base.connects_to(database: { default: :primary, readonly: :readonly })
|
||||
|
||||
@pool = ActiveRecord::Base.connection_handlers[:default].retrieve_connection_pool("ActiveRecord::Base")
|
||||
assert_not_nil @pool
|
||||
assert_equal "test/db/primary.sqlite3", @pool.db_config.database
|
||||
|
||||
@pool2 = ActiveRecord::Base.connection_handlers[:readonly].retrieve_connection_pool("ActiveRecord::Base")
|
||||
assert_not_nil @pool2
|
||||
assert_equal "test/db/readonly.sqlite3", @pool2.db_config.database
|
||||
ensure
|
||||
ActiveRecord::Base.configurations = @prev_configs
|
||||
ActiveRecord::Base.establish_connection(:arunit)
|
||||
ENV["RAILS_ENV"] = previous_env
|
||||
end
|
||||
|
||||
def test_switching_connections_with_database_url
|
||||
previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
|
||||
previous_url, ENV["DATABASE_URL"] = ENV["DATABASE_URL"], "postgres://localhost/foo"
|
||||
|
||||
ActiveRecord::Base.connects_to(database: { writing: "postgres://localhost/bar" })
|
||||
assert_equal :writing, ActiveRecord::Base.current_role
|
||||
assert ActiveRecord::Base.connected_to?(role: :writing)
|
||||
|
||||
handler = ActiveRecord::Base.connection_handler
|
||||
assert_equal handler, ActiveRecord::Base.connection_handlers[:writing]
|
||||
|
||||
assert_not_nil pool = handler.retrieve_connection_pool("ActiveRecord::Base")
|
||||
assert_equal({ adapter: "postgresql", database: "bar", host: "localhost" }, pool.db_config.configuration_hash)
|
||||
ensure
|
||||
ActiveRecord::Base.establish_connection(:arunit)
|
||||
ENV["RAILS_ENV"] = previous_env
|
||||
ENV["DATABASE_URL"] = previous_url
|
||||
end
|
||||
|
||||
def test_switching_connections_with_database_config_hash
|
||||
previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
|
||||
config = { adapter: "sqlite3", database: "test/db/readonly.sqlite3" }
|
||||
|
||||
ActiveRecord::Base.connects_to(database: { writing: config })
|
||||
assert_equal :writing, ActiveRecord::Base.current_role
|
||||
assert ActiveRecord::Base.connected_to?(role: :writing)
|
||||
|
||||
handler = ActiveRecord::Base.connection_handler
|
||||
assert_equal handler, ActiveRecord::Base.connection_handlers[:writing]
|
||||
|
||||
assert_not_nil pool = handler.retrieve_connection_pool("ActiveRecord::Base")
|
||||
assert_equal(config, pool.db_config.configuration_hash)
|
||||
ensure
|
||||
ActiveRecord::Base.establish_connection(:arunit)
|
||||
ENV["RAILS_ENV"] = previous_env
|
||||
end
|
||||
|
||||
def test_switching_connections_without_database_and_role_raises
|
||||
error = assert_raises(ArgumentError) do
|
||||
ActiveRecord::Base.connected_to { }
|
||||
end
|
||||
assert_equal "must provide a `shard` and/or `role`.", error.message
|
||||
end
|
||||
|
||||
def test_switching_connections_with_database_symbol_uses_default_role
|
||||
previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
|
||||
|
||||
config = {
|
||||
"default_env" => {
|
||||
"animals" => { adapter: "sqlite3", database: "test/db/animals.sqlite3" },
|
||||
"primary" => { adapter: "sqlite3", database: "test/db/primary.sqlite3" }
|
||||
}
|
||||
}
|
||||
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
|
||||
|
||||
ActiveRecord::Base.connects_to(database: { writing: :animals })
|
||||
assert_equal :writing, ActiveRecord::Base.current_role
|
||||
assert ActiveRecord::Base.connected_to?(role: :writing)
|
||||
|
||||
handler = ActiveRecord::Base.connection_handler
|
||||
assert_equal handler, ActiveRecord::Base.connection_handlers[:writing]
|
||||
|
||||
assert_not_nil pool = handler.retrieve_connection_pool("ActiveRecord::Base")
|
||||
assert_equal(config["default_env"]["animals"], pool.db_config.configuration_hash)
|
||||
ensure
|
||||
ActiveRecord::Base.configurations = @prev_configs
|
||||
ActiveRecord::Base.establish_connection(:arunit)
|
||||
ENV["RAILS_ENV"] = previous_env
|
||||
end
|
||||
|
||||
def test_switching_connections_with_database_hash_uses_passed_role_and_database
|
||||
previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
|
||||
|
||||
config = {
|
||||
"default_env" => {
|
||||
"animals" => { adapter: "sqlite3", database: "test/db/animals.sqlite3" },
|
||||
"primary" => { adapter: "sqlite3", database: "test/db/primary.sqlite3" }
|
||||
}
|
||||
}
|
||||
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
|
||||
|
||||
ActiveRecord::Base.connects_to(database: { writing: :primary })
|
||||
assert_equal :writing, ActiveRecord::Base.current_role
|
||||
assert ActiveRecord::Base.connected_to?(role: :writing)
|
||||
|
||||
handler = ActiveRecord::Base.connection_handler
|
||||
assert_equal handler, ActiveRecord::Base.connection_handlers[:writing]
|
||||
|
||||
assert_not_nil pool = handler.retrieve_connection_pool("ActiveRecord::Base")
|
||||
assert_equal(config["default_env"]["primary"], pool.db_config.configuration_hash)
|
||||
ensure
|
||||
ActiveRecord::Base.configurations = @prev_configs
|
||||
ActiveRecord::Base.establish_connection(:arunit)
|
||||
ENV["RAILS_ENV"] = previous_env
|
||||
end
|
||||
|
||||
def test_connects_to_with_single_configuration
|
||||
config = {
|
||||
"development" => { "adapter" => "sqlite3", "database" => "test/db/primary.sqlite3" },
|
||||
}
|
||||
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
|
||||
|
||||
ActiveRecord::Base.connects_to database: { writing: :development }
|
||||
|
||||
assert_equal 1, ActiveRecord::Base.connection_handlers.size
|
||||
assert_equal ActiveRecord::Base.connection_handler, ActiveRecord::Base.connection_handlers[:writing]
|
||||
assert_equal :writing, ActiveRecord::Base.current_role
|
||||
assert ActiveRecord::Base.connected_to?(role: :writing)
|
||||
ensure
|
||||
ActiveRecord::Base.configurations = @prev_configs
|
||||
ActiveRecord::Base.establish_connection(:arunit)
|
||||
end
|
||||
|
||||
def test_connects_to_using_top_level_key_in_two_level_config
|
||||
config = {
|
||||
"development" => { "adapter" => "sqlite3", "database" => "test/db/primary.sqlite3" },
|
||||
"development_readonly" => { "adapter" => "sqlite3", "database" => "test/db/readonly.sqlite3" }
|
||||
}
|
||||
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
|
||||
|
||||
ActiveRecord::Base.connects_to database: { writing: :development, reading: :development_readonly }
|
||||
|
||||
@pool = ActiveRecord::Base.connection_handlers[:reading].retrieve_connection_pool("ActiveRecord::Base")
|
||||
assert_not_nil @pool
|
||||
assert_equal "test/db/readonly.sqlite3", @pool.db_config.database
|
||||
ensure
|
||||
ActiveRecord::Base.configurations = @prev_configs
|
||||
ActiveRecord::Base.establish_connection(:arunit)
|
||||
end
|
||||
|
||||
def test_connects_to_returns_array_of_established_connections
|
||||
config = {
|
||||
"development" => { "adapter" => "sqlite3", "database" => "test/db/primary.sqlite3" },
|
||||
"development_readonly" => { "adapter" => "sqlite3", "database" => "test/db/readonly.sqlite3" }
|
||||
}
|
||||
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
|
||||
|
||||
result = ActiveRecord::Base.connects_to database: { writing: :development, reading: :development_readonly }
|
||||
|
||||
assert_equal(
|
||||
[
|
||||
ActiveRecord::Base.connection_handlers[:writing].retrieve_connection_pool("ActiveRecord::Base"),
|
||||
ActiveRecord::Base.connection_handlers[:reading].retrieve_connection_pool("ActiveRecord::Base")
|
||||
],
|
||||
result
|
||||
)
|
||||
ensure
|
||||
ActiveRecord::Base.configurations = @prev_configs
|
||||
ActiveRecord::Base.establish_connection(:arunit)
|
||||
end
|
||||
end
|
||||
|
||||
def test_connection_pools
|
||||
assert_equal([@rw_pool], @handlers[:writing].connection_pools)
|
||||
assert_equal([@ro_pool], @handlers[:reading].connection_pools)
|
||||
end
|
||||
|
||||
def test_retrieve_connection
|
||||
assert @rw_handler.retrieve_connection(@owner_name)
|
||||
assert @ro_handler.retrieve_connection(@owner_name)
|
||||
end
|
||||
|
||||
def test_active_connections?
|
||||
assert_not_predicate @rw_handler, :active_connections?
|
||||
assert_not_predicate @ro_handler, :active_connections?
|
||||
|
||||
assert @rw_handler.retrieve_connection(@owner_name)
|
||||
assert @ro_handler.retrieve_connection(@owner_name)
|
||||
|
||||
assert_predicate @rw_handler, :active_connections?
|
||||
assert_predicate @ro_handler, :active_connections?
|
||||
|
||||
@rw_handler.clear_active_connections!
|
||||
|
||||
assert_not_predicate @rw_handler, :active_connections?
|
||||
|
||||
@ro_handler.clear_active_connections!
|
||||
|
||||
assert_not_predicate @ro_handler, :active_connections?
|
||||
end
|
||||
|
||||
def test_retrieve_connection_pool
|
||||
assert_not_nil @rw_handler.retrieve_connection_pool(@owner_name)
|
||||
assert_not_nil @ro_handler.retrieve_connection_pool(@owner_name)
|
||||
end
|
||||
|
||||
def test_retrieve_connection_pool_with_invalid_id
|
||||
assert_nil @rw_handler.retrieve_connection_pool("foo")
|
||||
assert_nil @ro_handler.retrieve_connection_pool("foo")
|
||||
end
|
||||
|
||||
def test_connection_handlers_are_per_thread_and_not_per_fiber
|
||||
assert_deprecated do
|
||||
ActiveRecord::Base.connection_handlers = { writing: ActiveRecord::Base.default_connection_handler, reading: ActiveRecord::ConnectionAdapters::ConnectionHandler.new }
|
||||
end
|
||||
|
||||
reading_handler = ActiveRecord::Base.connection_handlers[:reading]
|
||||
|
||||
reading = ActiveRecord::Base.connected_to(role: :reading) do
|
||||
Person.connection_handler
|
||||
end
|
||||
|
||||
assert_not_equal reading, ActiveRecord::Base.connection_handler
|
||||
assert_equal reading, reading_handler
|
||||
end
|
||||
|
||||
def test_connection_handlers_swapping_connections_in_fiber
|
||||
assert_deprecated do
|
||||
ActiveRecord::Base.connection_handlers = { writing: ActiveRecord::Base.default_connection_handler, reading: ActiveRecord::ConnectionAdapters::ConnectionHandler.new }
|
||||
end
|
||||
|
||||
reading_handler = ActiveRecord::Base.connection_handlers[:reading]
|
||||
|
||||
enum = Enumerator.new do |r|
|
||||
r << ActiveRecord::Base.connection_handler
|
||||
end
|
||||
|
||||
reading = ActiveRecord::Base.connected_to(role: :reading) do
|
||||
enum.next
|
||||
end
|
||||
|
||||
assert_equal reading, reading_handler
|
||||
end
|
||||
|
||||
def test_calling_connected_to_on_a_non_existent_handler_raises
|
||||
error = assert_raises ActiveRecord::ConnectionNotEstablished do
|
||||
ActiveRecord::Base.connected_to(role: :reading) do
|
||||
Person.first
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal "No connection pool for 'ActiveRecord::Base' found for the 'reading' role.", error.message
|
||||
end
|
||||
|
||||
def test_default_handlers_are_writing_and_reading
|
||||
assert_equal :writing, ActiveRecord.writing_role
|
||||
assert_equal :reading, ActiveRecord.reading_role
|
||||
end
|
||||
|
||||
def test_an_application_can_change_the_default_handlers
|
||||
old_writing = ActiveRecord.writing_role
|
||||
old_reading = ActiveRecord.reading_role
|
||||
ActiveRecord.writing_role = :default
|
||||
ActiveRecord.reading_role = :readonly
|
||||
|
||||
assert_equal :default, ActiveRecord.writing_role
|
||||
assert_equal :readonly, ActiveRecord.reading_role
|
||||
ensure
|
||||
ActiveRecord.writing_role = old_writing
|
||||
ActiveRecord.reading_role = old_reading
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,588 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "cases/helper"
|
||||
require "models/person"
|
||||
|
||||
module ActiveRecord
|
||||
module ConnectionAdapters
|
||||
class LegacyConnectionHandlersShardingDbTest < ActiveRecord::TestCase
|
||||
self.use_transactional_tests = false
|
||||
|
||||
fixtures :people
|
||||
|
||||
def setup
|
||||
@legacy_setting = ActiveRecord.legacy_connection_handling
|
||||
ActiveRecord.legacy_connection_handling = true
|
||||
assert_deprecated do
|
||||
ActiveRecord::Base.connection_handlers = { writing: ActiveRecord::Base.default_connection_handler }
|
||||
end
|
||||
|
||||
@handlers = { writing: ConnectionHandler.new, reading: ConnectionHandler.new }
|
||||
@rw_handler = @handlers[:writing]
|
||||
@ro_handler = @handlers[:reading]
|
||||
@owner_name = "ActiveRecord::Base"
|
||||
db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
|
||||
|
||||
@rw_pool = @handlers[:writing].establish_connection(db_config)
|
||||
@ro_pool = @handlers[:reading].establish_connection(db_config)
|
||||
end
|
||||
|
||||
def teardown
|
||||
clean_up_legacy_connection_handlers
|
||||
ActiveRecord.legacy_connection_handling = @legacy_setting
|
||||
end
|
||||
|
||||
unless in_memory_db?
|
||||
def test_establishing_a_connection_in_connected_to_block_uses_current_role_and_shard
|
||||
ActiveRecord::Base.connected_to(role: :writing, shard: :shard_one) do
|
||||
db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
|
||||
SecondaryBase.establish_connection(db_config)
|
||||
|
||||
assert_equal [:shard_one], ActiveRecord::Base.connection_handlers[:writing].send(:owner_to_pool_manager).fetch("ActiveRecord::ConnectionAdapters::LegacyConnectionHandlersShardingDbTest::SecondaryBase").instance_variable_get(:@name_to_pool_config).keys
|
||||
end
|
||||
end
|
||||
|
||||
def test_establish_connection_using_3_levels_config
|
||||
previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
|
||||
|
||||
config = {
|
||||
"default_env" => {
|
||||
"primary" => { "adapter" => "sqlite3", "database" => "test/db/primary.sqlite3" },
|
||||
"primary_shard_one" => { "adapter" => "sqlite3", "database" => "test/db/primary_shard_one.sqlite3" },
|
||||
}
|
||||
}
|
||||
|
||||
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
|
||||
|
||||
ActiveRecord::Base.connects_to(shards: {
|
||||
default: { writing: :primary },
|
||||
shard_one: { writing: :primary_shard_one }
|
||||
})
|
||||
|
||||
connection_handlers = ActiveRecord::Base.connection_handlers
|
||||
|
||||
base_pool = connection_handlers[:writing].retrieve_connection_pool("ActiveRecord::Base")
|
||||
default_pool = connection_handlers[:writing].retrieve_connection_pool("ActiveRecord::Base", shard: :default)
|
||||
|
||||
assert_equal base_pool, default_pool
|
||||
assert_equal "test/db/primary.sqlite3", default_pool.db_config.database
|
||||
assert_equal "primary", default_pool.db_config.name
|
||||
|
||||
assert_not_nil pool = connection_handlers[:writing].retrieve_connection_pool("ActiveRecord::Base", shard: :shard_one)
|
||||
assert_equal "test/db/primary_shard_one.sqlite3", pool.db_config.database
|
||||
assert_equal "primary_shard_one", pool.db_config.name
|
||||
ensure
|
||||
ActiveRecord::Base.configurations = @prev_configs
|
||||
ActiveRecord::Base.establish_connection(:arunit)
|
||||
ENV["RAILS_ENV"] = previous_env
|
||||
end
|
||||
|
||||
def test_establish_connection_using_3_levels_config_with_shards_and_replica
|
||||
previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
|
||||
|
||||
config = {
|
||||
"default_env" => {
|
||||
"primary" => { "adapter" => "sqlite3", "database" => "test/db/primary.sqlite3" },
|
||||
"primary_replica" => { "adapter" => "sqlite3", "database" => "test/db/primary.sqlite3", "replica" => true },
|
||||
"primary_shard_one" => { "adapter" => "sqlite3", "database" => "test/db/primary_shard_one.sqlite3" },
|
||||
"primary_shard_one_replica" => { "adapter" => "sqlite3", "database" => "test/db/primary_shard_one.sqlite3", "replica" => true }
|
||||
}
|
||||
}
|
||||
|
||||
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
|
||||
|
||||
ActiveRecord::Base.connects_to(shards: {
|
||||
default: { writing: :primary, reading: :primary_replica },
|
||||
shard_one: { writing: :primary_shard_one, reading: :primary_shard_one_replica }
|
||||
})
|
||||
|
||||
connection_handlers = ActiveRecord::Base.connection_handlers
|
||||
|
||||
default_writing_pool = connection_handlers[:writing].retrieve_connection_pool("ActiveRecord::Base", shard: :default)
|
||||
base_writing_pool = connection_handlers[:writing].retrieve_connection_pool("ActiveRecord::Base")
|
||||
assert_equal base_writing_pool, default_writing_pool
|
||||
assert_equal "test/db/primary.sqlite3", default_writing_pool.db_config.database
|
||||
assert_equal "primary", default_writing_pool.db_config.name
|
||||
|
||||
default_reading_pool = connection_handlers[:reading].retrieve_connection_pool("ActiveRecord::Base", shard: :default)
|
||||
base_reading_pool = connection_handlers[:reading].retrieve_connection_pool("ActiveRecord::Base")
|
||||
assert_equal base_reading_pool, default_reading_pool
|
||||
assert_equal "test/db/primary.sqlite3", default_reading_pool.db_config.database
|
||||
assert_equal "primary_replica", default_reading_pool.db_config.name
|
||||
|
||||
assert_not_nil pool = connection_handlers[:writing].retrieve_connection_pool("ActiveRecord::Base", shard: :shard_one)
|
||||
assert_equal "test/db/primary_shard_one.sqlite3", pool.db_config.database
|
||||
assert_equal "primary_shard_one", pool.db_config.name
|
||||
|
||||
assert_not_nil pool = connection_handlers[:reading].retrieve_connection_pool("ActiveRecord::Base", shard: :shard_one)
|
||||
assert_equal "test/db/primary_shard_one.sqlite3", pool.db_config.database
|
||||
assert_equal "primary_shard_one_replica", pool.db_config.name
|
||||
ensure
|
||||
ActiveRecord::Base.configurations = @prev_configs
|
||||
ActiveRecord::Base.establish_connection(:arunit)
|
||||
ENV["RAILS_ENV"] = previous_env
|
||||
end
|
||||
|
||||
def test_switching_connections_via_handler
|
||||
previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
|
||||
|
||||
config = {
|
||||
"default_env" => {
|
||||
"primary" => { "adapter" => "sqlite3", "database" => "test/db/primary.sqlite3" },
|
||||
"primary_replica" => { "adapter" => "sqlite3", "database" => "test/db/primary.sqlite3", "replica" => true },
|
||||
"primary_shard_one" => { "adapter" => "sqlite3", "database" => "test/db/primary_shard_one.sqlite3" },
|
||||
"primary_shard_one_replica" => { "adapter" => "sqlite3", "database" => "test/db/primary_shard_one.sqlite3", "replica" => true }
|
||||
}
|
||||
}
|
||||
|
||||
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
|
||||
|
||||
ActiveRecord::Base.connects_to(shards: {
|
||||
default: { writing: :primary, reading: :primary_replica },
|
||||
shard_one: { writing: :primary_shard_one, reading: :primary_shard_one_replica }
|
||||
})
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :reading, shard: :default) do
|
||||
@ro_handler = ActiveRecord::Base.connection_handler
|
||||
assert_equal ActiveRecord::Base.connection_handler, ActiveRecord::Base.connection_handlers[:reading]
|
||||
assert_equal :reading, ActiveRecord::Base.current_role
|
||||
assert ActiveRecord::Base.connected_to?(role: :reading, shard: :default)
|
||||
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :default)
|
||||
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :shard_one)
|
||||
assert_not ActiveRecord::Base.connected_to?(role: :reading, shard: :shard_one)
|
||||
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :writing, shard: :default) do
|
||||
assert_equal ActiveRecord::Base.connection_handler, ActiveRecord::Base.connection_handlers[:writing]
|
||||
assert_not_equal @ro_handler, ActiveRecord::Base.connection_handler
|
||||
assert_equal :writing, ActiveRecord::Base.current_role
|
||||
assert ActiveRecord::Base.connected_to?(role: :writing, shard: :default)
|
||||
assert_not ActiveRecord::Base.connected_to?(role: :reading, shard: :default)
|
||||
assert_not ActiveRecord::Base.connected_to?(role: :reading, shard: :shard_one)
|
||||
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :shard_one)
|
||||
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :reading, shard: :shard_one) do
|
||||
@ro_handler = ActiveRecord::Base.connection_handler
|
||||
assert_equal ActiveRecord::Base.connection_handler, ActiveRecord::Base.connection_handlers[:reading]
|
||||
assert_equal :reading, ActiveRecord::Base.current_role
|
||||
assert ActiveRecord::Base.connected_to?(role: :reading, shard: :shard_one)
|
||||
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :shard_one)
|
||||
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :default)
|
||||
assert_not ActiveRecord::Base.connected_to?(role: :reading, shard: :default)
|
||||
assert_predicate ActiveRecord::Base.connection, :preventing_writes?
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :writing, shard: :shard_one) do
|
||||
assert_equal ActiveRecord::Base.connection_handler, ActiveRecord::Base.connection_handlers[:writing]
|
||||
assert_not_equal @ro_handler, ActiveRecord::Base.connection_handler
|
||||
assert_equal :writing, ActiveRecord::Base.current_role
|
||||
assert ActiveRecord::Base.connected_to?(role: :writing, shard: :shard_one)
|
||||
assert_not ActiveRecord::Base.connected_to?(role: :reading, shard: :shard_one)
|
||||
assert_not ActiveRecord::Base.connected_to?(role: :reading, shard: :default)
|
||||
assert_not ActiveRecord::Base.connected_to?(role: :writing, shard: :default)
|
||||
assert_not_predicate ActiveRecord::Base.connection, :preventing_writes?
|
||||
end
|
||||
ensure
|
||||
ActiveRecord::Base.configurations = @prev_configs
|
||||
ActiveRecord::Base.establish_connection(:arunit)
|
||||
ENV["RAILS_ENV"] = previous_env
|
||||
end
|
||||
|
||||
def test_retrieves_proper_connection_with_nested_connected_to
|
||||
previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
|
||||
|
||||
config = {
|
||||
"default_env" => {
|
||||
"primary" => { "adapter" => "sqlite3", "database" => "test/db/primary.sqlite3" },
|
||||
"primary_replica" => { "adapter" => "sqlite3", "database" => "test/db/primary.sqlite3", "replica" => true },
|
||||
"primary_shard_one" => { "adapter" => "sqlite3", "database" => "test/db/primary_shard_one.sqlite3" },
|
||||
"primary_shard_one_replica" => { "adapter" => "sqlite3", "database" => "test/db/primary_shard_one.sqlite3", "replica" => true }
|
||||
}
|
||||
}
|
||||
|
||||
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
|
||||
|
||||
ActiveRecord::Base.connects_to(shards: {
|
||||
default: { writing: :primary, reading: :primary_replica },
|
||||
shard_one: { writing: :primary_shard_one, reading: :primary_shard_one_replica }
|
||||
})
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :reading, shard: :shard_one) do
|
||||
# Uses the correct connection
|
||||
assert_equal "primary_shard_one_replica", ActiveRecord::Base.connection_pool.db_config.name
|
||||
|
||||
# Uses the shard currently in use
|
||||
ActiveRecord::Base.connected_to(role: :writing) do
|
||||
assert_equal "primary_shard_one", ActiveRecord::Base.connection_pool.db_config.name
|
||||
end
|
||||
|
||||
# Allows overriding the shard as well
|
||||
ActiveRecord::Base.connected_to(role: :reading, shard: :default) do
|
||||
assert_equal "primary_replica", ActiveRecord::Base.connection_pool.db_config.name
|
||||
end
|
||||
|
||||
# Resets correctly
|
||||
assert_equal "primary_shard_one_replica", ActiveRecord::Base.connection_pool.db_config.name
|
||||
end
|
||||
ensure
|
||||
ActiveRecord::Base.configurations = @prev_configs
|
||||
ActiveRecord::Base.establish_connection(:arunit)
|
||||
ENV["RAILS_ENV"] = previous_env
|
||||
end
|
||||
|
||||
def test_connected_to_raises_without_a_shard_or_role
|
||||
error = assert_raises(ArgumentError) do
|
||||
ActiveRecord::Base.connected_to { }
|
||||
end
|
||||
assert_equal "must provide a `shard` and/or `role`.", error.message
|
||||
end
|
||||
|
||||
def test_connects_to_raises_with_a_shard_and_database_key
|
||||
error = assert_raises(ArgumentError) do
|
||||
ActiveRecord::Base.connects_to(database: { writing: :arunit }, shards: { shard_one: { writing: :arunit } })
|
||||
end
|
||||
assert_equal "`connects_to` can only accept a `database` or `shards` argument, but not both arguments.", error.message
|
||||
end
|
||||
|
||||
def test_retrieve_connection_pool_with_invalid_shard
|
||||
assert_not_nil @rw_handler.retrieve_connection_pool("ActiveRecord::Base")
|
||||
assert_nil @rw_handler.retrieve_connection_pool("ActiveRecord::Base", shard: :foo)
|
||||
|
||||
assert_not_nil @ro_handler.retrieve_connection_pool("ActiveRecord::Base")
|
||||
assert_nil @ro_handler.retrieve_connection_pool("ActiveRecord::Base", shard: :foo)
|
||||
end
|
||||
|
||||
def test_calling_connected_to_on_a_non_existent_shard_raises
|
||||
ActiveRecord::Base.connects_to(shards: {
|
||||
default: { writing: :arunit, reading: :arunit }
|
||||
})
|
||||
|
||||
error = assert_raises ActiveRecord::ConnectionNotEstablished do
|
||||
ActiveRecord::Base.connected_to(role: :reading, shard: :foo) do
|
||||
Person.first
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal "No connection pool for 'ActiveRecord::Base' found for the 'foo' shard.", error.message
|
||||
end
|
||||
|
||||
def test_retrieves_proper_connection_with_nested_connected_to_on_abstract_classes
|
||||
previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
|
||||
|
||||
config = {
|
||||
"default_env" => {
|
||||
"primary" => { "adapter" => "sqlite3", "database" => "test/db/primary.sqlite3" },
|
||||
"primary_replica" => { "adapter" => "sqlite3", "database" => "test/db/primary.sqlite3", "replica" => true },
|
||||
"primary_shard_one" => { "adapter" => "sqlite3", "database" => "test/db/primary_shard_one.sqlite3" },
|
||||
"primary_shard_one_replica" => { "adapter" => "sqlite3", "database" => "test/db/primary_shard_one.sqlite3", "replica" => true },
|
||||
"primary_shard_two" => { "adapter" => "sqlite3", "database" => "test/db/primary_shard_two.sqlite3" },
|
||||
"primary_shard_two_replica" => { "adapter" => "sqlite3", "database" => "test/db/primary_shard_two.sqlite3", "replica" => true },
|
||||
"secondary" => { "adapter" => "sqlite3", "database" => "test/db/secondary.sqlite3" },
|
||||
"secondary_replica" => { "adapter" => "sqlite3", "database" => "test/db/secondary.sqlite3", "replica" => true },
|
||||
"secondary_shard_one" => { "adapter" => "sqlite3", "database" => "test/db/secondary_shard_one.sqlite3" },
|
||||
"secondary_shard_one_replica" => { "adapter" => "sqlite3", "database" => "test/db/secondary_shard_one.sqlite3", "replica" => true },
|
||||
"secondary_shard_two" => { "adapter" => "sqlite3", "database" => "test/db/secondary_shard_two.sqlite3" },
|
||||
"secondary_shard_two_replica" => { "adapter" => "sqlite3", "database" => "test/db/secondary_shard_two.sqlite3", "replica" => true }
|
||||
}
|
||||
}
|
||||
|
||||
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
|
||||
|
||||
ActiveRecord::Base.connects_to(shards: {
|
||||
default: { writing: :primary, reading: :primary_replica },
|
||||
shard_one: { writing: :primary_shard_one, reading: :primary_shard_one_replica }
|
||||
})
|
||||
|
||||
SecondaryBase.connects_to(shards: {
|
||||
default: { writing: :secondary, reading: :secondary_replica },
|
||||
shard_one: { writing: :secondary_shard_one, reading: :secondary_shard_one_replica },
|
||||
shard_two: { writing: :secondary_shard_two, reading: :secondary_shard_two_replica }
|
||||
})
|
||||
|
||||
assert_equal "primary", ActiveRecord::Base.connection_pool.db_config.name
|
||||
assert_equal "secondary", SecondaryBase.connection_pool.db_config.name
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :reading, shard: :shard_one) do
|
||||
# ActiveRecord:Base is a global switch for passed values
|
||||
assert_equal "primary_shard_one_replica", ActiveRecord::Base.connection_pool.db_config.name
|
||||
assert_equal "secondary_shard_one_replica", SecondaryBase.connection_pool.db_config.name
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :writing, shard: :default) do
|
||||
assert_equal "primary", ActiveRecord::Base.connection_pool.db_config.name
|
||||
assert_equal "secondary", SecondaryBase.connection_pool.db_config.name
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :reading, shard: :shard_two) do
|
||||
# ActiveRecord::Base has no shard_two
|
||||
assert_equal "secondary_shard_two_replica", SecondaryBase.connection_pool.db_config.name
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :writing) do
|
||||
assert_equal "primary", ActiveRecord::Base.connection_pool.db_config.name
|
||||
assert_equal "secondary", SecondaryBase.connection_pool.db_config.name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal "primary", ActiveRecord::Base.connection_pool.db_config.name
|
||||
assert_equal "secondary", SecondaryBase.connection_pool.db_config.name
|
||||
ensure
|
||||
ActiveRecord::Base.configurations = @prev_configs
|
||||
ActiveRecord::Base.establish_connection(:arunit)
|
||||
ENV["RAILS_ENV"] = previous_env
|
||||
end
|
||||
end
|
||||
|
||||
class SecondaryBase < ActiveRecord::Base
|
||||
self.abstract_class = true
|
||||
end
|
||||
|
||||
class ShardConnectionTestModel < SecondaryBase
|
||||
end
|
||||
|
||||
class SomeOtherBase < ActiveRecord::Base
|
||||
self.abstract_class = true
|
||||
end
|
||||
|
||||
class ShardConnectionTestModelB < SomeOtherBase
|
||||
end
|
||||
|
||||
def test_same_shards_across_clusters
|
||||
SecondaryBase.connects_to shards: { one: { writing: { database: ":memory:", adapter: "sqlite3" } } }
|
||||
SomeOtherBase.connects_to shards: { one: { writing: { database: ":memory:", adapter: "sqlite3" } } }
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :writing, shard: :one) do
|
||||
ShardConnectionTestModel.connection.execute("CREATE TABLE `shard_connection_test_models` (shard_key VARCHAR (255))")
|
||||
ShardConnectionTestModel.create!(shard_key: "test_model_default")
|
||||
|
||||
ShardConnectionTestModelB.connection.execute("CREATE TABLE `shard_connection_test_model_bs` (shard_key VARCHAR (255))")
|
||||
ShardConnectionTestModelB.create!(shard_key: "test_model_b_default")
|
||||
|
||||
assert_equal "test_model_default", ShardConnectionTestModel.where(shard_key: "test_model_default").first.shard_key
|
||||
assert_equal "test_model_b_default", ShardConnectionTestModelB.where(shard_key: "test_model_b_default").first.shard_key
|
||||
end
|
||||
end
|
||||
|
||||
def test_sharding_separation
|
||||
SecondaryBase.connects_to shards: {
|
||||
default: { writing: { database: ":memory:", adapter: "sqlite3" } },
|
||||
one: { writing: { database: ":memory:", adapter: "sqlite3" } }
|
||||
}
|
||||
|
||||
[:default, :one].each do |shard_name|
|
||||
ActiveRecord::Base.connected_to(role: :writing, shard: shard_name) do
|
||||
ShardConnectionTestModel.connection.execute("CREATE TABLE `shard_connection_test_models` (shard_key VARCHAR (255))")
|
||||
end
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :writing) do
|
||||
# Create a record on :default
|
||||
ShardConnectionTestModel.create!(shard_key: "foo")
|
||||
end
|
||||
|
||||
# Make sure we can read it when explicitly connecting to :default
|
||||
ActiveRecord::Base.connected_to(role: :writing, shard: :default) do
|
||||
assert ShardConnectionTestModel.find_by_shard_key("foo")
|
||||
end
|
||||
|
||||
# Switch to shard and make sure we can't read the record from :default
|
||||
# Also add a new record on :one
|
||||
ActiveRecord::Base.connected_to(role: :writing, shard: :one) do
|
||||
assert_not ShardConnectionTestModel.find_by_shard_key("foo")
|
||||
ShardConnectionTestModel.create!(shard_key: "bar")
|
||||
end
|
||||
|
||||
# Make sure we can't read the record from :one but can read the record
|
||||
# from :default
|
||||
assert_not ShardConnectionTestModel.find_by_shard_key("bar")
|
||||
assert ShardConnectionTestModel.find_by_shard_key("foo")
|
||||
end
|
||||
|
||||
def test_swapping_shards_globally_in_a_multi_threaded_environment
|
||||
tf_default = Tempfile.open "shard_key_default"
|
||||
tf_shard_one = Tempfile.open "shard_key_one"
|
||||
|
||||
SecondaryBase.connects_to shards: {
|
||||
default: { writing: { database: tf_default.path, adapter: "sqlite3" } },
|
||||
one: { writing: { database: tf_shard_one.path, adapter: "sqlite3" } }
|
||||
}
|
||||
|
||||
[:default, :one].each do |shard_name|
|
||||
ActiveRecord::Base.connected_to(role: :writing, shard: shard_name) do
|
||||
ShardConnectionTestModel.connection.execute("CREATE TABLE `shard_connection_test_models` (shard_key VARCHAR (255))")
|
||||
ShardConnectionTestModel.connection.execute("INSERT INTO `shard_connection_test_models` VALUES ('shard_key_#{shard_name}')")
|
||||
end
|
||||
end
|
||||
|
||||
shard_one_latch = Concurrent::CountDownLatch.new
|
||||
shard_default_latch = Concurrent::CountDownLatch.new
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :writing) do
|
||||
ShardConnectionTestModel.connection
|
||||
end
|
||||
|
||||
thread = Thread.new do
|
||||
ActiveRecord::Base.connected_to(role: :writing) do
|
||||
ShardConnectionTestModel.connection
|
||||
|
||||
shard_default_latch.wait
|
||||
assert_equal "shard_key_default", ShardConnectionTestModel.connection.select_value("SELECT shard_key from shard_connection_test_models")
|
||||
shard_one_latch.count_down
|
||||
end
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :writing, shard: :one) do
|
||||
shard_default_latch.count_down
|
||||
assert_equal "shard_key_one", ShardConnectionTestModel.connection.select_value("SELECT shard_key from shard_connection_test_models")
|
||||
shard_one_latch.wait
|
||||
end
|
||||
|
||||
thread.join
|
||||
ensure
|
||||
tf_shard_one.close
|
||||
tf_shard_one.unlink
|
||||
tf_default.close
|
||||
tf_default.unlink
|
||||
end
|
||||
|
||||
def test_swapping_shards_and_roles_in_a_multi_threaded_environment
|
||||
tf_default = Tempfile.open "shard_key_default"
|
||||
tf_shard_one = Tempfile.open "shard_key_one"
|
||||
tf_default_reading = Tempfile.open "shard_key_default_reading"
|
||||
tf_shard_one_reading = Tempfile.open "shard_key_one_reading"
|
||||
|
||||
SecondaryBase.connects_to shards: {
|
||||
default: { writing: { database: tf_default.path, adapter: "sqlite3" }, secondary: { database: tf_default_reading.path, adapter: "sqlite3" } },
|
||||
one: { writing: { database: tf_shard_one.path, adapter: "sqlite3" }, secondary: { database: tf_shard_one_reading.path, adapter: "sqlite3" } }
|
||||
}
|
||||
|
||||
[:default, :one].each do |shard_name|
|
||||
ActiveRecord::Base.connected_to(role: :writing, shard: shard_name) do
|
||||
ShardConnectionTestModel.connection.execute("CREATE TABLE `shard_connection_test_models` (shard_key VARCHAR (255))")
|
||||
ShardConnectionTestModel.connection.execute("INSERT INTO `shard_connection_test_models` VALUES ('shard_key_#{shard_name}')")
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :secondary, shard: shard_name) do
|
||||
ShardConnectionTestModel.connection.execute("CREATE TABLE `shard_connection_test_models` (shard_key VARCHAR (255))")
|
||||
ShardConnectionTestModel.connection.execute("INSERT INTO `shard_connection_test_models` VALUES ('shard_key_#{shard_name}_secondary')")
|
||||
end
|
||||
end
|
||||
|
||||
shard_one_latch = Concurrent::CountDownLatch.new
|
||||
shard_default_latch = Concurrent::CountDownLatch.new
|
||||
|
||||
ShardConnectionTestModel.connection
|
||||
|
||||
thread = Thread.new do
|
||||
ShardConnectionTestModel.connection
|
||||
|
||||
shard_default_latch.wait
|
||||
assert_equal "shard_key_default", ShardConnectionTestModel.connection.select_value("SELECT shard_key from shard_connection_test_models")
|
||||
shard_one_latch.count_down
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connected_to(shard: :one, role: :secondary) do
|
||||
shard_default_latch.count_down
|
||||
assert_equal "shard_key_one_secondary", ShardConnectionTestModel.connection.select_value("SELECT shard_key from shard_connection_test_models")
|
||||
shard_one_latch.wait
|
||||
end
|
||||
|
||||
thread.join
|
||||
ensure
|
||||
tf_shard_one.close
|
||||
tf_shard_one.unlink
|
||||
tf_default.close
|
||||
tf_default.unlink
|
||||
tf_shard_one_reading.close
|
||||
tf_shard_one_reading.unlink
|
||||
tf_default_reading.close
|
||||
tf_default_reading.unlink
|
||||
end
|
||||
|
||||
def test_swapping_granular_shards_and_roles_in_a_multi_threaded_environment
|
||||
tf_default = Tempfile.open "shard_key_default"
|
||||
tf_shard_one = Tempfile.open "shard_key_one"
|
||||
tf_default_reading = Tempfile.open "shard_key_default_reading"
|
||||
tf_shard_one_reading = Tempfile.open "shard_key_one_reading"
|
||||
tf_default2 = Tempfile.open "shard_key_default2"
|
||||
tf_shard_one2 = Tempfile.open "shard_key_one2"
|
||||
tf_default_reading2 = Tempfile.open "shard_key_default_reading2"
|
||||
tf_shard_one_reading2 = Tempfile.open "shard_key_one_reading2"
|
||||
|
||||
SecondaryBase.connects_to shards: {
|
||||
default: { writing: { database: tf_default.path, adapter: "sqlite3" }, secondary: { database: tf_default_reading.path, adapter: "sqlite3" } },
|
||||
one: { writing: { database: tf_shard_one.path, adapter: "sqlite3" }, secondary: { database: tf_shard_one_reading.path, adapter: "sqlite3" } }
|
||||
}
|
||||
|
||||
SomeOtherBase.connects_to shards: {
|
||||
default: { writing: { database: tf_default2.path, adapter: "sqlite3" }, secondary: { database: tf_default_reading2.path, adapter: "sqlite3" } },
|
||||
one: { writing: { database: tf_shard_one2.path, adapter: "sqlite3" }, secondary: { database: tf_shard_one_reading2.path, adapter: "sqlite3" } }
|
||||
}
|
||||
|
||||
[:default, :one].each do |shard_name|
|
||||
ActiveRecord::Base.connected_to(role: :writing, shard: shard_name) do
|
||||
ShardConnectionTestModel.connection.execute("CREATE TABLE `shard_connection_test_models` (shard_key VARCHAR (255))")
|
||||
ShardConnectionTestModel.connection.execute("INSERT INTO `shard_connection_test_models` VALUES ('shard_key_#{shard_name}')")
|
||||
ShardConnectionTestModelB.connection.execute("CREATE TABLE `shard_connection_test_models` (shard_key VARCHAR (255))")
|
||||
ShardConnectionTestModelB.connection.execute("INSERT INTO `shard_connection_test_models` VALUES ('shard_key_#{shard_name}_b')")
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :secondary, shard: shard_name) do
|
||||
ShardConnectionTestModel.connection.execute("CREATE TABLE `shard_connection_test_models` (shard_key VARCHAR (255))")
|
||||
ShardConnectionTestModel.connection.execute("INSERT INTO `shard_connection_test_models` VALUES ('shard_key_#{shard_name}_secondary')")
|
||||
ShardConnectionTestModelB.connection.execute("CREATE TABLE `shard_connection_test_models` (shard_key VARCHAR (255))")
|
||||
ShardConnectionTestModelB.connection.execute("INSERT INTO `shard_connection_test_models` VALUES ('shard_key_#{shard_name}_secondary_b')")
|
||||
end
|
||||
end
|
||||
|
||||
shard_one_latch = Concurrent::CountDownLatch.new
|
||||
shard_default_latch = Concurrent::CountDownLatch.new
|
||||
|
||||
ShardConnectionTestModel.connection
|
||||
|
||||
thread = Thread.new do
|
||||
ShardConnectionTestModel.connection
|
||||
|
||||
shard_default_latch.wait
|
||||
assert_equal "shard_key_default", ShardConnectionTestModel.connection.select_value("SELECT shard_key from shard_connection_test_models")
|
||||
assert_equal "shard_key_default_b", ShardConnectionTestModelB.connection.select_value("SELECT shard_key from shard_connection_test_models")
|
||||
shard_one_latch.count_down
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connected_to(shard: :one, role: :secondary) do
|
||||
shard_default_latch.count_down
|
||||
|
||||
assert_equal "shard_key_one_secondary", ShardConnectionTestModel.connection.select_value("SELECT shard_key from shard_connection_test_models")
|
||||
assert_equal "shard_key_one_secondary_b", ShardConnectionTestModelB.connection.select_value("SELECT shard_key from shard_connection_test_models")
|
||||
|
||||
ActiveRecord::Base.connected_to(shard: :one, role: :secondary) do
|
||||
assert_equal "shard_key_one_secondary", ShardConnectionTestModel.connection.select_value("SELECT shard_key from shard_connection_test_models")
|
||||
assert_equal "shard_key_one_secondary_b", ShardConnectionTestModelB.connection.select_value("SELECT shard_key from shard_connection_test_models")
|
||||
end
|
||||
|
||||
shard_one_latch.wait
|
||||
end
|
||||
thread.join
|
||||
ensure
|
||||
tf_shard_one.close
|
||||
tf_shard_one.unlink
|
||||
tf_default.close
|
||||
tf_default.unlink
|
||||
tf_shard_one_reading.close
|
||||
tf_shard_one_reading.unlink
|
||||
tf_default_reading.close
|
||||
tf_default_reading.unlink
|
||||
tf_shard_one2.close
|
||||
tf_shard_one2.unlink
|
||||
tf_default2.close
|
||||
tf_default2.unlink
|
||||
tf_shard_one_reading2.close
|
||||
tf_shard_one_reading2.unlink
|
||||
tf_default_reading2.close
|
||||
tf_default_reading2.unlink
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1621,126 +1621,4 @@ if current_adapter?(:SQLite3Adapter) && !in_memory_db?
|
|||
default_config.merge("replica" => true)
|
||||
end
|
||||
end
|
||||
|
||||
class MultipleFixtureLegacyConnectionsTest < ActiveRecord::TestCase
|
||||
include ActiveRecord::TestFixtures
|
||||
|
||||
fixtures :dogs
|
||||
|
||||
def setup
|
||||
@old_value = ActiveRecord.legacy_connection_handling
|
||||
ActiveRecord.legacy_connection_handling = true
|
||||
|
||||
@old_handler = ActiveRecord::Base.connection_handler
|
||||
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
|
||||
db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new(ENV["RAILS_ENV"], "readonly", readonly_config)
|
||||
|
||||
teardown_shared_connection_pool
|
||||
|
||||
handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
|
||||
ActiveRecord::Base.connection_handler = handler
|
||||
handler.establish_connection(db_config)
|
||||
assert_deprecated do
|
||||
ActiveRecord::Base.connection_handlers = {}
|
||||
end
|
||||
ActiveRecord::Base.connects_to(database: { writing: :default, reading: :readonly })
|
||||
|
||||
setup_shared_connection_pool
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveRecord::Base.configurations = @prev_configs
|
||||
ActiveRecord::Base.connection_handler = @old_handler
|
||||
clean_up_legacy_connection_handlers
|
||||
ActiveRecord.legacy_connection_handling = false
|
||||
end
|
||||
|
||||
def test_uses_writing_connection_for_fixtures
|
||||
ActiveRecord::Base.connected_to(role: :reading) do
|
||||
Dog.first
|
||||
|
||||
assert_nothing_raised do
|
||||
ActiveRecord::Base.connected_to(role: :writing) { Dog.create! alias: "Doggo" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_writing_and_reading_connections_are_the_same_with_legacy_handling
|
||||
writing = ActiveRecord::Base.connection_handlers[:writing]
|
||||
reading = ActiveRecord::Base.connection_handlers[:reading]
|
||||
|
||||
rw_conn = writing.retrieve_connection_pool("ActiveRecord::Base").connection
|
||||
ro_conn = reading.retrieve_connection_pool("ActiveRecord::Base").connection
|
||||
|
||||
assert_equal rw_conn, ro_conn
|
||||
|
||||
teardown_shared_connection_pool
|
||||
|
||||
rw_conn = writing.retrieve_connection_pool("ActiveRecord::Base").connection
|
||||
ro_conn = reading.retrieve_connection_pool("ActiveRecord::Base").connection
|
||||
|
||||
assert_not_equal rw_conn, ro_conn
|
||||
end
|
||||
|
||||
def test_writing_and_reading_connections_are_the_same_for_non_default_shards_with_legacy_handling
|
||||
ActiveRecord::Base.connects_to shards: {
|
||||
default: { writing: :default, reading: :readonly },
|
||||
two: { writing: :default, reading: :readonly }
|
||||
}
|
||||
|
||||
writing = ActiveRecord::Base.connection_handlers[:writing]
|
||||
reading = ActiveRecord::Base.connection_handlers[:reading]
|
||||
|
||||
rw_conn = writing.retrieve_connection_pool("ActiveRecord::Base", shard: :two).connection
|
||||
ro_conn = reading.retrieve_connection_pool("ActiveRecord::Base", shard: :two).connection
|
||||
|
||||
assert_equal rw_conn, ro_conn
|
||||
|
||||
teardown_shared_connection_pool
|
||||
|
||||
rw_conn = writing.retrieve_connection_pool("ActiveRecord::Base", shard: :two).connection
|
||||
ro_conn = reading.retrieve_connection_pool("ActiveRecord::Base", shard: :two).connection
|
||||
|
||||
assert_not_equal rw_conn, ro_conn
|
||||
end
|
||||
|
||||
def test_only_existing_connections_are_replaced
|
||||
ActiveRecord::Base.connects_to shards: {
|
||||
default: { writing: :default, reading: :readonly },
|
||||
two: { writing: :default }
|
||||
}
|
||||
|
||||
setup_shared_connection_pool
|
||||
|
||||
assert_raises(ActiveRecord::ConnectionNotEstablished) do
|
||||
ActiveRecord::Base.connected_to(role: :reading, shard: :two) do
|
||||
ActiveRecord::Base.retrieve_connection
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_only_existing_connections_are_restored
|
||||
clean_up_legacy_connection_handlers
|
||||
teardown_shared_connection_pool
|
||||
|
||||
assert_raises(ActiveRecord::ConnectionNotEstablished) do
|
||||
ActiveRecord::Base.connected_to(role: :reading) do
|
||||
ActiveRecord::Base.retrieve_connection
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def config
|
||||
{ "default" => default_config, "readonly" => readonly_config }
|
||||
end
|
||||
|
||||
def default_config
|
||||
{ "adapter" => "sqlite3", "database" => "test/fixtures/fixture_database.sqlite3" }
|
||||
end
|
||||
|
||||
def readonly_config
|
||||
default_config.merge("replica" => true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -168,19 +168,6 @@ def disable_extension!(extension, connection)
|
|||
connection.reconnect!
|
||||
end
|
||||
|
||||
def clean_up_legacy_connection_handlers
|
||||
handler = ActiveRecord::Base.default_connection_handler
|
||||
assert_deprecated do
|
||||
ActiveRecord::Base.connection_handlers = {}
|
||||
end
|
||||
|
||||
handler.connection_pool_names.each do |name|
|
||||
next if ["ActiveRecord::Base", "ARUnit2Model", "Contact", "ContactSti", "FirstAbstractClass", "SecondAbstractClass"].include?(name)
|
||||
|
||||
handler.send(:owner_to_pool_manager).delete(name)
|
||||
end
|
||||
end
|
||||
|
||||
def clean_up_connection_handler
|
||||
handler = ActiveRecord::Base.connection_handler
|
||||
handler.instance_variable_get(:@owner_to_pool_manager).each do |owner, pool_manager|
|
||||
|
|
|
@ -74,36 +74,6 @@ class QueryCacheTest < ActiveRecord::TestCase
|
|||
assert_cache :off
|
||||
end
|
||||
|
||||
def test_query_cache_is_applied_to_legacy_connections_in_all_handlers
|
||||
old_value = ActiveRecord.legacy_connection_handling
|
||||
ActiveRecord.legacy_connection_handling = true
|
||||
|
||||
assert_deprecated do
|
||||
ActiveRecord::Base.connection_handlers = {
|
||||
writing: ActiveRecord::Base.default_connection_handler,
|
||||
reading: ActiveRecord::ConnectionAdapters::ConnectionHandler.new
|
||||
}
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :reading) do
|
||||
db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
|
||||
ActiveRecord::Base.establish_connection(db_config)
|
||||
end
|
||||
|
||||
mw = middleware { |env|
|
||||
reading_handler = ActiveRecord::Base.connection_handlers[:reading]
|
||||
ro_pool = reading_handler.connection_pool_list
|
||||
ro_conn = ro_pool.first.connection
|
||||
assert_predicate ActiveRecord::Base.connection, :query_cache_enabled
|
||||
assert_predicate ro_conn, :query_cache_enabled
|
||||
}
|
||||
|
||||
mw.call({})
|
||||
ensure
|
||||
clean_up_legacy_connection_handlers
|
||||
ActiveRecord.legacy_connection_handling = old_value
|
||||
end
|
||||
|
||||
def test_query_cache_is_applied_to_all_connections
|
||||
ActiveRecord::Base.connected_to(role: :reading) do
|
||||
db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
|
||||
|
@ -125,74 +95,6 @@ class QueryCacheTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
if Process.respond_to?(:fork) && !in_memory_db?
|
||||
def test_query_cache_with_multiple_handlers_and_forked_processes_legacy_handling
|
||||
old_value = ActiveRecord.legacy_connection_handling
|
||||
ActiveRecord.legacy_connection_handling = true
|
||||
|
||||
assert_deprecated do
|
||||
ActiveRecord::Base.connection_handlers = {
|
||||
writing: ActiveRecord::Base.default_connection_handler,
|
||||
reading: ActiveRecord::ConnectionAdapters::ConnectionHandler.new
|
||||
}
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :reading) do
|
||||
db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
|
||||
ActiveRecord::Base.establish_connection(db_config)
|
||||
end
|
||||
|
||||
rd, wr = IO.pipe
|
||||
rd.binmode
|
||||
wr.binmode
|
||||
|
||||
pid = fork {
|
||||
rd.close
|
||||
status = 0
|
||||
|
||||
middleware { |env|
|
||||
begin
|
||||
assert_cache :clean
|
||||
|
||||
# first request dirties cache
|
||||
ActiveRecord::Base.connected_to(role: :reading) do
|
||||
Post.first
|
||||
assert_cache :dirty
|
||||
end
|
||||
|
||||
# should clear the cache
|
||||
Post.create!(title: "a new post", body: "and a body")
|
||||
|
||||
# fails because cache is still dirty
|
||||
ActiveRecord::Base.connected_to(role: :reading) do
|
||||
assert_cache :clean
|
||||
Post.first
|
||||
end
|
||||
|
||||
rescue Minitest::Assertion => e
|
||||
wr.write Marshal.dump e
|
||||
status = 1
|
||||
end
|
||||
}.call({})
|
||||
|
||||
wr.close
|
||||
exit!(status)
|
||||
}
|
||||
|
||||
wr.close
|
||||
|
||||
Process.waitpid pid
|
||||
if !$?.success?
|
||||
raise Marshal.load(rd.read)
|
||||
else
|
||||
assert_predicate $?, :success?
|
||||
end
|
||||
|
||||
rd.close
|
||||
ensure
|
||||
clean_up_legacy_connection_handlers
|
||||
ActiveRecord.legacy_connection_handling = old_value
|
||||
end
|
||||
|
||||
def test_query_cache_with_forked_processes
|
||||
ActiveRecord::Base.connected_to(role: :reading) do
|
||||
db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
|
||||
|
@ -665,51 +567,6 @@ class QueryCacheTest < ActiveRecord::TestCase
|
|||
}.call({})
|
||||
end
|
||||
|
||||
def test_clear_query_cache_is_called_on_all_legacy_connections
|
||||
skip "with in memory db, reading role won't be able to see database on writing role" if in_memory_db?
|
||||
old_value = ActiveRecord.legacy_connection_handling
|
||||
ActiveRecord.legacy_connection_handling = true
|
||||
|
||||
assert_deprecated do
|
||||
ActiveRecord::Base.connection_handlers = {
|
||||
writing: ActiveRecord::Base.default_connection_handler,
|
||||
reading: ActiveRecord::ConnectionAdapters::ConnectionHandler.new
|
||||
}
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :reading) do
|
||||
db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
|
||||
ActiveRecord::Base.establish_connection(db_config)
|
||||
end
|
||||
|
||||
mw = middleware { |env|
|
||||
ActiveRecord::Base.connected_to(role: :reading) do
|
||||
@topic = Topic.first
|
||||
end
|
||||
|
||||
assert @topic
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :writing) do
|
||||
@topic.title = "Topic title"
|
||||
@topic.save!
|
||||
end
|
||||
|
||||
assert_equal "Topic title", @topic.title
|
||||
|
||||
ActiveRecord::Base.connected_to(role: :reading) do
|
||||
@topic = Topic.first
|
||||
assert_equal "Topic title", @topic.title
|
||||
end
|
||||
}
|
||||
|
||||
mw.call({})
|
||||
ensure
|
||||
unless in_memory_db?
|
||||
clean_up_legacy_connection_handlers
|
||||
ActiveRecord.legacy_connection_handling = old_value
|
||||
end
|
||||
end
|
||||
|
||||
def test_clear_query_cache_is_called_on_all_connections
|
||||
skip "with in memory db, reading role won't be able to see database on writing role" if in_memory_db?
|
||||
|
||||
|
|
|
@ -22,46 +22,6 @@ class TestFixturesTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
unless in_memory_db?
|
||||
def test_doesnt_rely_on_active_support_test_case_specific_methods_with_legacy_connection_handling
|
||||
old_value = ActiveRecord.legacy_connection_handling
|
||||
ActiveRecord.legacy_connection_handling = true
|
||||
|
||||
tmp_dir = Dir.mktmpdir
|
||||
File.write(File.join(tmp_dir, "zines.yml"), <<~YML)
|
||||
going_out:
|
||||
title: Hello
|
||||
YML
|
||||
|
||||
klass = Class.new(Minitest::Test) do
|
||||
include ActiveRecord::TestFixtures
|
||||
|
||||
self.fixture_path = tmp_dir
|
||||
self.use_transactional_tests = true
|
||||
|
||||
fixtures :all
|
||||
|
||||
def test_run_successfully
|
||||
assert_equal("Hello", Zine.first.title)
|
||||
assert_equal("Hello", zines(:going_out).title)
|
||||
end
|
||||
end
|
||||
|
||||
old_handler = ActiveRecord::Base.connection_handler
|
||||
ActiveRecord::Base.connection_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
|
||||
assert_deprecated do
|
||||
ActiveRecord::Base.connection_handlers = {}
|
||||
end
|
||||
ActiveRecord::Base.establish_connection(:arunit)
|
||||
|
||||
test_result = klass.new("test_run_successfully").run
|
||||
assert_predicate(test_result, :passed?)
|
||||
ensure
|
||||
clean_up_legacy_connection_handlers
|
||||
ActiveRecord::Base.connection_handler = old_handler
|
||||
FileUtils.rm_r(tmp_dir)
|
||||
ActiveRecord.legacy_connection_handling = old_value
|
||||
end
|
||||
|
||||
def test_doesnt_rely_on_active_support_test_case_specific_methods
|
||||
tmp_dir = Dir.mktmpdir
|
||||
File.write(File.join(tmp_dir, "zines.yml"), <<~YML)
|
||||
|
|
|
@ -19,7 +19,6 @@ module ARTest
|
|||
end
|
||||
|
||||
def self.connect
|
||||
ActiveRecord.legacy_connection_handling = false
|
||||
ActiveRecord.async_query_executor = :global_thread_pool
|
||||
puts "Using #{connection_name}"
|
||||
ActiveRecord::Base.logger = ActiveSupport::Logger.new("debug.log", 0, 100 * 1024 * 1024)
|
||||
|
|
|
@ -84,6 +84,8 @@ Please refer to the [Changelog][active-record] for detailed changes.
|
|||
|
||||
### Removals
|
||||
|
||||
* Remove support for `ActiveRecord.legacy_connection_handling`.
|
||||
|
||||
### Deprecations
|
||||
|
||||
### Notable changes
|
||||
|
|
|
@ -10,7 +10,6 @@ After reading this guide you will know:
|
|||
* How to set up your application for multiple databases.
|
||||
* How automatic connection switching works.
|
||||
* How to use horizontal sharding for multiple databases.
|
||||
* How to migrate from `legacy_connection_handling` to the new connection handling.
|
||||
* What features are supported and what's still a work in progress.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
@ -477,41 +476,12 @@ config.active_record.shard_resolver = ->(request) {
|
|||
}
|
||||
```
|
||||
|
||||
## Migrate to the new connection handling
|
||||
|
||||
In Rails 6.1+, Active Record provides a new internal API for connection management.
|
||||
In most cases applications will not need to make any changes except to opt-in to the
|
||||
new behavior (if upgrading from 6.0 and below) by setting
|
||||
[`config.active_record.legacy_connection_handling`][] to `false`. If you have a single database
|
||||
application, no other changes will be required. If you have a multiple database application
|
||||
the following changes are required if your application is using these methods:
|
||||
|
||||
* `connection_handlers` and `connection_handlers=` no longer works in the new connection
|
||||
handling. If you were calling a method on one of the connection handlers, for example,
|
||||
`connection_handlers[:reading].retrieve_connection_pool("ActiveRecord::Base")`
|
||||
you will now need to update that call to be
|
||||
`connection_handlers.retrieve_connection_pool("ActiveRecord::Base", role: :reading)`.
|
||||
* Calls to `ActiveRecord::Base.connection_handler.prevent_writes` will need to be updated
|
||||
to `ActiveRecord::Base.connection.preventing_writes?`.
|
||||
* If you need all the pools, including writing and reading, a new method has been provided on
|
||||
the handler. Call `connection_handler.all_connection_pools` to use this. In most cases though
|
||||
you'll want writing or reading pools with `connection_handler.connection_pool_list(:writing)` or
|
||||
`connection_handler.connection_pool_list(:reading)`.
|
||||
* If you turn off `legacy_connection_handling` in your application, any method that's unsupported
|
||||
will raise an error (i.e. `connection_handlers=`).
|
||||
|
||||
[`config.active_record.legacy_connection_handling`]: configuring.html#config-active-record-legacy-connection-handling
|
||||
|
||||
## Granular Database Connection Switching
|
||||
|
||||
In Rails 6.1 it's possible to switch connections for one database instead of
|
||||
all databases globally. To use this feature you must first set
|
||||
[`config.active_record.legacy_connection_handling`][] to `false` in your application
|
||||
configuration. The majority of applications should not need to make any other
|
||||
changes since the public APIs have the same behavior. See the above section for
|
||||
how to enable and migrate away from `legacy_connection_handling`.
|
||||
all databases globally.
|
||||
|
||||
With `legacy_connection_handling` set to `false`, any abstract connection class
|
||||
With granular database connection switching, any abstract connection class
|
||||
will be able to switch connections without affecting other connections. This
|
||||
is useful for switching your `AnimalsRecord` queries to read from the replica
|
||||
while ensuring your `ApplicationRecord` queries go to the primary.
|
||||
|
|
|
@ -163,7 +163,9 @@ module Rails
|
|||
|
||||
if respond_to?(:active_record)
|
||||
active_record.has_many_inversing = true
|
||||
active_record.legacy_connection_handling = false
|
||||
if respond_to?(:legacy_connection_handling)
|
||||
active_record.legacy_connection_handling = false
|
||||
end
|
||||
end
|
||||
|
||||
if respond_to?(:active_job)
|
||||
|
|
|
@ -3372,21 +3372,6 @@ module ApplicationTests
|
|||
assert_equal false, Rails.application.config.assets.unknown_asset_fallback
|
||||
end
|
||||
|
||||
test "legacy_connection_handling is false by default for new apps" do
|
||||
app "development"
|
||||
|
||||
assert_equal false, Rails.application.config.active_record.legacy_connection_handling
|
||||
end
|
||||
|
||||
test "legacy_connection_handling is not set before 6.1" do
|
||||
remove_from_config '.*config\.load_defaults.*\n'
|
||||
add_to_config 'config.load_defaults "6.0"'
|
||||
|
||||
app "development"
|
||||
|
||||
assert_nil Rails.application.config.active_record.legacy_connection_handling
|
||||
end
|
||||
|
||||
test "ActionDispatch::Request.return_only_media_type_on_content_type is false by default" do
|
||||
app "development"
|
||||
|
||||
|
|
Loading…
Reference in New Issue