未验证 提交 74688afe 编写于 作者: E Eileen M. Uchitelle 提交者: GitHub

Merge pull request #37622 from eileencodes/multi-role-per-class

Add an intermediary called RoleManager to manage connections
......@@ -9,7 +9,8 @@ module ConnectionAdapters
end
autoload :Column
autoload :Role
autoload :PoolConfig
autoload :PoolManager
autoload :Resolver
autoload_at "active_record/connection_adapters/abstract/schema_definitions" do
......
......@@ -370,21 +370,21 @@ def run
include ConnectionAdapters::AbstractPool
attr_accessor :automatic_reconnect, :checkout_timeout
attr_reader :db_config, :size, :reaper, :role
attr_reader :db_config, :size, :reaper, :pool_config
delegate :schema_cache, :schema_cache=, to: :role
delegate :schema_cache, :schema_cache=, to: :pool_config
# Creates a new ConnectionPool object. +role+ is a Role
# Creates a new ConnectionPool object. +pool_config+ is a PoolConfig
# object which describes database connection information (e.g. adapter,
# host name, username, password, etc), as well as the maximum size for
# this ConnectionPool.
#
# The default ConnectionPool maximum size is 5.
def initialize(role)
def initialize(pool_config)
super()
@role = role
@db_config = role.db_config
@pool_config = pool_config
@db_config = pool_config.db_config
@checkout_timeout = db_config.checkout_timeout
@idle_timeout = db_config.idle_timeout
......@@ -1003,8 +1003,8 @@ class ConnectionHandler
private_constant :FINALIZER
def initialize
# These caches are keyed by role.connection_specification_name (Role#connection_specification_name).
@owner_to_role = Concurrent::Map.new(initial_capacity: 2)
# These caches are keyed by pool_config.connection_specification_name (PoolConfig#connection_specification_name).
@owner_to_pool_manager = Concurrent::Map.new(initial_capacity: 2)
# Backup finalizer: if the forked child skipped Kernel#fork the early discard has not occurred
ObjectSpace.define_finalizer self, FINALIZER
......@@ -1031,34 +1031,36 @@ def while_preventing_writes(enabled = true)
end
def connection_pool_names # :nodoc:
owner_to_role.keys
owner_to_pool_manager.keys
end
def connection_pool_list
owner_to_role.values.compact.map(&:pool)
owner_to_pool_manager.values.compact.flat_map { |m| m.pool_configs.map(&:pool) }
end
alias :connection_pools :connection_pool_list
def establish_connection(config)
def establish_connection(config, pool_key = :default)
resolver = Resolver.new(Base.configurations)
role = resolver.resolve_role(config)
db_config = role.db_config
pool_config = resolver.resolve_pool_config(config)
db_config = pool_config.db_config
remove_connection(role.connection_specification_name)
remove_connection(pool_config.connection_specification_name, pool_key)
message_bus = ActiveSupport::Notifications.instrumenter
payload = {
connection_id: object_id
}
if role
payload[:spec_name] = role.connection_specification_name
if pool_config
payload[:spec_name] = pool_config.connection_specification_name
payload[:config] = db_config.configuration_hash
end
owner_to_role[role.connection_specification_name] = role
owner_to_pool_manager[pool_config.connection_specification_name] ||= PoolManager.new
pool_manager = owner_to_pool_manager[pool_config.connection_specification_name]
pool_manager.set_pool_config(pool_key, pool_config)
message_bus.instrument("!connection.active_record", payload) do
role.pool
pool_config.pool
end
end
......@@ -1114,8 +1116,8 @@ def retrieve_connection(spec_name) #:nodoc:
# Returns true if a connection that's accessible to this class has
# already been opened.
def connected?(spec_name)
pool = retrieve_connection_pool(spec_name)
def connected?(spec_name, pool_key = :default)
pool = retrieve_connection_pool(spec_name, pool_key)
pool && pool.connected?
end
......@@ -1123,22 +1125,27 @@ def connected?(spec_name)
# connection and the defined connection (if they exist). The result
# can be used as an argument for #establish_connection, for easily
# re-establishing the connection.
def remove_connection(spec_name)
if role = owner_to_role.delete(spec_name)
role.disconnect!
role.db_config.configuration_hash
def remove_connection(spec_name, pool_key = :default)
if pool_manager = owner_to_pool_manager[spec_name]
pool_config = pool_manager.remove_pool_config(pool_key)
if pool_config
pool_config.disconnect!
pool_config.db_config.configuration_hash
end
end
end
# Retrieving the connection pool happens a lot, so we cache it in @owner_to_role.
# Retrieving the connection pool happens a lot, so we cache it in @owner_to_pool_manager.
# This makes retrieving the connection pool O(1) once the process is warm.
# When a connection is established or removed, we invalidate the cache.
def retrieve_connection_pool(spec_name)
owner_to_role[spec_name]&.pool
def retrieve_connection_pool(spec_name, pool_key = :default)
pool_config = owner_to_pool_manager[spec_name]&.get_pool_config(pool_key)
pool_config&.pool
end
private
attr_reader :owner_to_role
attr_reader :owner_to_pool_manager
end
end
end
......@@ -2,7 +2,7 @@
module ActiveRecord
module ConnectionAdapters
class Role # :nodoc:
class PoolConfig # :nodoc:
include Mutex_m
attr_reader :db_config, :connection_specification_name
......@@ -60,4 +60,4 @@ def discard_pool!
end
end
ActiveSupport::ForkTracker.after_fork { ActiveRecord::ConnectionAdapters::Role.discard_pools! }
ActiveSupport::ForkTracker.after_fork { ActiveRecord::ConnectionAdapters::PoolConfig.discard_pools! }
# frozen_string_literal: true
module ActiveRecord
module ConnectionAdapters
class PoolManager # :nodoc:
def initialize
@name_to_pool_config = {}
end
def pool_configs
@name_to_pool_config.values
end
def remove_pool_config(key)
@name_to_pool_config.delete(key)
end
def get_pool_config(key)
@name_to_pool_config[key]
end
def set_pool_config(key, pool_config)
@name_to_pool_config[key] = pool_config
end
end
end
end
......@@ -2,7 +2,7 @@
module ActiveRecord
module ConnectionAdapters
# Builds a Role from user input.
# Builds a PoolConfig from user input.
class Resolver # :nodoc:
attr_reader :configurations
......@@ -11,17 +11,17 @@ def initialize(configurations)
@configurations = configurations
end
# Returns an instance of Role for a given adapter.
# Returns an instance of PoolConfig for a given adapter.
# Accepts a hash one layer deep that contains all connection information.
#
# == Example
#
# config = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
# role = Resolver.new(config).resolve_role(:production)
# role.db_config.configuration_hash
# pool_config = Resolver.new(config).resolve_pool_config(:production)
# pool_config.db_config.configuration_hash
# # => { host: "localhost", database: "foo", adapter: "sqlite3" }
#
def resolve_role(config)
def resolve_pool_config(config)
pool_name = config if config.is_a?(Symbol)
db_config = resolve(config, pool_name)
......@@ -53,7 +53,7 @@ def resolve_role(config)
raise AdapterNotFound, "database configuration specifies nonexistent #{db_config.adapter} adapter"
end
Role.new(db_config.configuration_hash.delete(:name) || "primary", db_config)
PoolConfig.new(db_config.configuration_hash.delete(:name) || "primary", db_config)
end
# Returns fully resolved connection, accepts hash, string or symbol.
......
......@@ -192,7 +192,11 @@ def setup_shared_connection_pool
ActiveRecord::Base.connection_handlers.values.each do |handler|
if handler != writing_handler
handler.connection_pool_names.each do |name|
handler.send(:owner_to_role)[name] = writing_handler.send(:owner_to_role)[name]
writing_pool_manager = writing_handler.send(:owner_to_pool_manager)[name]
writing_pool_config = writing_pool_manager.get_pool_config(:default)
pool_manager = handler.send(:owner_to_pool_manager)[name]
pool_manager.set_pool_config(:default, writing_pool_config)
end
end
end
......
......@@ -40,8 +40,8 @@ def test_expire_mutates_in_use
def test_close
db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new("test", "primary", {})
role = ActiveRecord::ConnectionAdapters::Role.new("primary", db_config)
pool = Pool.new(role)
pool_config = ActiveRecord::ConnectionAdapters::PoolConfig.new("primary", db_config)
pool = Pool.new(pool_config)
pool.insert_connection_for_test! @adapter
@adapter.pool = pool
......
# frozen_string_literal: true
require "cases/helper"
require "models/person"
module ActiveRecord
module ConnectionAdapters
class ConnectionHandlersMultiPoolConfigTest < ActiveRecord::TestCase
self.use_transactional_tests = false
fixtures :people
def setup
@handlers = { writing: ConnectionHandler.new }
@rw_handler = @handlers[:writing]
@spec_name = "primary"
@writing_handler = ActiveRecord::Base.connection_handlers[:writing]
end
def teardown
ActiveRecord::Base.connection_handlers = { writing: ActiveRecord::Base.default_connection_handler }
end
unless in_memory_db?
def test_establish_connection_with_pool_configs
previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
config = {
"default_env" => {
"primary" => { "adapter" => "sqlite3", "database" => "db/primary.sqlite3" }
}
}
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
@writing_handler.establish_connection(:primary)
@writing_handler.establish_connection(:primary, :pool_config_two)
default_pool = @writing_handler.retrieve_connection_pool("primary", :default)
other_pool = @writing_handler.retrieve_connection_pool("primary", :pool_config_two)
assert_not_nil default_pool
assert_not_equal default_pool, other_pool
# :default if passed with no key
assert_equal default_pool, @writing_handler.retrieve_connection_pool("primary")
ensure
ActiveRecord::Base.configurations = @prev_configs
ActiveRecord::Base.establish_connection(:arunit)
ENV["RAILS_ENV"] = previous_env
end
def test_remove_connection
previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
config = {
"default_env" => {
"primary" => { "adapter" => "sqlite3", "database" => "db/primary.sqlite3" }
}
}
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
@writing_handler.establish_connection(:primary)
@writing_handler.establish_connection(:primary, :pool_config_two)
# remove default
@writing_handler.remove_connection("primary")
assert_nil @writing_handler.retrieve_connection_pool("primary")
assert_not_nil @writing_handler.retrieve_connection_pool("primary", :pool_config_two)
ensure
ActiveRecord::Base.configurations = @prev_configs
ActiveRecord::Base.establish_connection(:arunit)
ENV["RAILS_ENV"] = previous_env
end
def test_connected?
previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
config = {
"default_env" => {
"primary" => { "adapter" => "sqlite3", "database" => "db/primary.sqlite3" }
}
}
@prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
@writing_handler.establish_connection(:primary)
@writing_handler.establish_connection(:primary, :pool_config_two)
# connect to default
@writing_handler.connection_pool_list.first.checkout
assert @writing_handler.connected?("primary")
assert @writing_handler.connected?("primary", :default)
assert_not @writing_handler.connected?("primary", :pool_config_two)
ensure
ActiveRecord::Base.configurations = @prev_configs
ActiveRecord::Base.establish_connection(:arunit)
ENV["RAILS_ENV"] = previous_env
FileUtils.rm_rf "db"
end
end
end
end
end
......@@ -13,8 +13,8 @@ def setup
# Keep a duplicate pool so we do not bother others
@db_config = ActiveRecord::Base.connection_pool.db_config
@role = ActiveRecord::ConnectionAdapters::Role.new("primary", @db_config)
@pool = ConnectionPool.new(@role)
@pool_config = ActiveRecord::ConnectionAdapters::PoolConfig.new("primary", @db_config)
@pool = ConnectionPool.new(@pool_config)
if in_memory_db?
# Separate connections to an in-memory database create an entirely new database,
......@@ -201,8 +201,8 @@ def test_reap_inactive
def test_idle_timeout_configuration
@pool.disconnect!
@db_config.configuration_hash.merge!(idle_timeout: "0.02")
role = ActiveRecord::ConnectionAdapters::Role.new("primary", @db_config)
@pool = ConnectionPool.new(role)
pool_config = ActiveRecord::ConnectionAdapters::PoolConfig.new("primary", @db_config)
@pool = ConnectionPool.new(pool_config)
idle_conn = @pool.checkout
@pool.checkin(idle_conn)
......@@ -226,8 +226,8 @@ def test_idle_timeout_configuration
def test_disable_flush
@pool.disconnect!
@db_config.configuration_hash.merge!(idle_timeout: -5)
role = ActiveRecord::ConnectionAdapters::Role.new("primary", @db_config)
@pool = ConnectionPool.new(role)
pool_config = ActiveRecord::ConnectionAdapters::PoolConfig.new("primary", @db_config)
@pool = ConnectionPool.new(pool_config)
idle_conn = @pool.checkout
@pool.checkin(idle_conn)
......@@ -317,7 +317,7 @@ def test_active_connection?
end
def test_checkout_behaviour
pool = ConnectionPool.new(@role)
pool = ConnectionPool.new(@pool_config)
main_connection = pool.connection
assert_not_nil main_connection
threads = []
......@@ -450,7 +450,7 @@ def test_checkout_fairness_by_group
end
def test_automatic_reconnect_restores_after_disconnect
pool = ConnectionPool.new(@role)
pool = ConnectionPool.new(@pool_config)
assert pool.automatic_reconnect
assert pool.connection
......@@ -459,7 +459,7 @@ def test_automatic_reconnect_restores_after_disconnect
end
def test_automatic_reconnect_can_be_disabled
pool = ConnectionPool.new(@role)
pool = ConnectionPool.new(@pool_config)
pool.disconnect!
pool.automatic_reconnect = false
......@@ -723,10 +723,10 @@ def with_single_connection_pool
old_config = @db_config.configuration_hash
db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new("arunit", "primary", old_config.dup)
db_config.configuration_hash[:pool] = 1 # this is safe to do, because .dupped Role also auto-dups its config
db_config.configuration_hash[:pool] = 1 # this is safe to do, because .dupped PoolConfig also auto-dups its config
role = ActiveRecord::ConnectionAdapters::Role.new("primary", db_config)
yield(pool = ConnectionPool.new(role))
pool_config = ActiveRecord::ConnectionAdapters::PoolConfig.new("primary", db_config)
yield(pool = ConnectionPool.new(pool_config))
ensure
pool.disconnect! if pool
end
......
......@@ -14,8 +14,8 @@ def setup
teardown do
return if in_memory_db?
role = ActiveRecord::Base.connection_config
ActiveRecord::Base.establish_connection(role)
config = ActiveRecord::Base.connection_config
ActiveRecord::Base.establish_connection(config)
end
unless in_memory_db?
......
......@@ -1414,10 +1414,10 @@ class MultipleDatabaseFixturesTest < ActiveRecord::TestCase
private
def with_temporary_connection_pool
role = ActiveRecord::Base.connection_handler.send(:owner_to_role).fetch("primary")
new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new(role)
pool_config = ActiveRecord::Base.connection_handler.send(:owner_to_pool_manager).fetch("primary").get_pool_config(:default)
new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new(pool_config)
role.stub(:pool, new_pool) do
pool_config.stub(:pool, new_pool) do
yield
end
end
......
......@@ -4,23 +4,23 @@
module ActiveRecord
module ConnectionAdapters
class Role
class PoolConfig
class ResolverTest < ActiveRecord::TestCase
def resolve(role, config = {})
def resolve(pool_config, config = {})
configs = ActiveRecord::DatabaseConfigurations.new(config)
resolver = ConnectionAdapters::Resolver.new(configs)
resolver.resolve(role, role).configuration_hash
resolver.resolve(pool_config, pool_config).configuration_hash
end
def resolve_role(role, config = {})
def resolve_pool_config(pool_config, config = {})
configs = ActiveRecord::DatabaseConfigurations.new(config)
resolver = ConnectionAdapters::Resolver.new(configs)
resolver.resolve_role(role)
resolver.resolve_pool_config(pool_config)
end
def test_url_invalid_adapter
error = assert_raises(LoadError) do
resolve_role "ridiculous://foo?encoding=utf8"
resolve_pool_config "ridiculous://foo?encoding=utf8"
end
assert_match "Could not load the 'ridiculous' Active Record adapter. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile.", error.message
......@@ -28,7 +28,7 @@ def test_url_invalid_adapter
def test_error_if_no_adapter_method
error = assert_raises(AdapterNotFound) do
resolve_role "abstract://foo?encoding=utf8"
resolve_pool_config "abstract://foo?encoding=utf8"
end
assert_match "database configuration specifies nonexistent abstract adapter", error.message
......@@ -38,121 +38,121 @@ def test_error_if_no_adapter_method
# checks that the adapter file can be required in.
def test_url_from_environment
role = resolve :production, "production" => "abstract://foo?encoding=utf8"
pool_config = resolve :production, "production" => "abstract://foo?encoding=utf8"
assert_equal({
adapter: "abstract",
host: "foo",
encoding: "utf8",
name: "production"
}, role)
}, pool_config)
end
def test_url_sub_key
role = resolve :production, "production" => { "url" => "abstract://foo?encoding=utf8" }
pool_config = resolve :production, "production" => { "url" => "abstract://foo?encoding=utf8" }
assert_equal({
adapter: "abstract",
host: "foo",
encoding: "utf8",
name: "production"
}, role)
}, pool_config)
end
def test_url_sub_key_merges_correctly
hash = { "url" => "abstract://foo?encoding=utf8&", "adapter" => "sqlite3", "host" => "bar", "pool" => "3" }
role = resolve :production, "production" => hash
pool_config = resolve :production, "production" => hash
assert_equal({
adapter: "abstract",
host: "foo",
encoding: "utf8",
pool: "3",
name: "production"
}, role)
}, pool_config)
end
def test_url_host_no_db
role = resolve "abstract://foo?encoding=utf8"
pool_config = resolve "abstract://foo?encoding=utf8"
assert_equal({
adapter: "abstract",
host: "foo",
encoding: "utf8"
}, role)
}, pool_config)
end
def test_url_missing_scheme
role = resolve "foo"
assert_equal({ database: "foo" }, role)
pool_config = resolve "foo"
assert_equal({ database: "foo" }, pool_config)
end
def test_url_host_db
role = resolve "abstract://foo/bar?encoding=utf8"
pool_config = resolve "abstract://foo/bar?encoding=utf8"
assert_equal({
adapter: "abstract",
database: "bar",
host: "foo",
encoding: "utf8"
}, role)
}, pool_config)
end
def test_url_port
role = resolve "abstract://foo:123?encoding=utf8"
pool_config = resolve "abstract://foo:123?encoding=utf8"
assert_equal({
adapter: "abstract",
port: 123,
host: "foo",
encoding: "utf8"
}, role)
}, pool_config)
end
def test_encoded_password
password = "am@z1ng_p@ssw0rd#!"
encoded_password = URI.encode_www_form_component(password)
role = resolve "abstract://foo:#{encoded_password}@localhost/bar"
assert_equal password, role[:password]
pool_config = resolve "abstract://foo:#{encoded_password}@localhost/bar"
assert_equal password, pool_config[:password]
end
def test_url_with_authority_for_sqlite3
role = resolve "sqlite3:///foo_test"
assert_equal("/foo_test", role[:database])
pool_config = resolve "sqlite3:///foo_test"
assert_equal("/foo_test", pool_config[:database])
end
def test_url_absolute_path_for_sqlite3
role = resolve "sqlite3:/foo_test"
assert_equal("/foo_test", role[:database])
pool_config = resolve "sqlite3:/foo_test"
assert_equal("/foo_test", pool_config[:database])
end
def test_url_relative_path_for_sqlite3
role = resolve "sqlite3:foo_test"
assert_equal("foo_test", role[:database])
pool_config = resolve "sqlite3:foo_test"
assert_equal("foo_test", pool_config[:database])
end
def test_url_memory_db_for_sqlite3
role = resolve "sqlite3::memory:"
assert_equal(":memory:", role[:database])
pool_config = resolve "sqlite3::memory:"
assert_equal(":memory:", pool_config[:database])
end
def test_url_sub_key_for_sqlite3
role = resolve :production, "production" => { "url" => "sqlite3:foo?encoding=utf8" }
pool_config = resolve :production, "production" => { "url" => "sqlite3:foo?encoding=utf8" }
assert_equal({
adapter: "sqlite3",
database: "foo",
encoding: "utf8",
name: "production"
}, role)
}, pool_config)
end
def test_role_connection_specification_name_on_key_lookup
role = resolve_role(:readonly, "readonly" => { "adapter" => "sqlite3" })
assert_equal "readonly", role.connection_specification_name
def test_pool_config_connection_specification_name_on_key_lookup
pool_config = resolve_pool_config(:readonly, "readonly" => { "adapter" => "sqlite3" })
assert_equal "readonly", pool_config.connection_specification_name
end
def test_role_connection_specification_name_with_inline_config
role = resolve_role("adapter" => "sqlite3")
assert_equal "primary", role.connection_specification_name, "should default to primary id"
def test_pool_config_connection_specification_name_with_inline_config
pool_config = resolve_pool_config("adapter" => "sqlite3")
assert_equal "primary", pool_config.connection_specification_name, "should default to primary id"
end
def test_role_with_invalid_type
def test_pool_config_with_invalid_type
assert_raises TypeError do
resolve_role(Object.new)
resolve_pool_config(Object.new)
end
end
end
......
......@@ -555,10 +555,10 @@ def test_clear_query_cache_is_called_on_all_connections
private
def with_temporary_connection_pool
role = ActiveRecord::Base.connection_handler.send(:owner_to_role).fetch("primary")
new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new(role)
pool_config = ActiveRecord::Base.connection_handler.send(:owner_to_pool_manager).fetch("primary").get_pool_config(:default)
new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new(pool_config)
role.stub(:pool, new_pool) do
pool_config.stub(:pool, new_pool) do
yield
end
end
......
......@@ -59,8 +59,8 @@ def test_some_time
def test_pool_has_reaper
config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", spec_name: "primary")
role = Role.new("primary", config)
pool = ConnectionPool.new(role)
pool_config = PoolConfig.new("primary", config)
pool = ConnectionPool.new(pool_config)
assert pool.reaper
ensure
......@@ -68,10 +68,10 @@ def test_pool_has_reaper
end
def test_reaping_frequency_configuration
role = duplicated_role
role.db_config.configuration_hash[:reaping_frequency] = "10.01"
pool_config = duplicated_pool_config
pool_config.db_config.configuration_hash[:reaping_frequency] = "10.01"
pool = ConnectionPool.new(role)
pool = ConnectionPool.new(pool_config)
assert_equal 10.01, pool.reaper.frequency
ensure
......@@ -79,10 +79,10 @@ def test_reaping_frequency_configuration
end
def test_connection_pool_starts_reaper
role = duplicated_role
role.db_config.configuration_hash[:reaping_frequency] = "0.0001"
pool_config = duplicated_pool_config
pool_config.db_config.configuration_hash[:reaping_frequency] = "0.0001"
pool = ConnectionPool.new(role)
pool = ConnectionPool.new(pool_config)
conn, child = new_conn_in_thread(pool)
......@@ -97,11 +97,11 @@ def test_connection_pool_starts_reaper
end
def test_reaper_works_after_pool_discard
role = duplicated_role
role.db_config.configuration_hash[:reaping_frequency] = "0.0001"
pool_config = duplicated_pool_config
pool_config.db_config.configuration_hash[:reaping_frequency] = "0.0001"
2.times do
pool = ConnectionPool.new(role)
pool = ConnectionPool.new(pool_config)
conn, child = new_conn_in_thread(pool)
......@@ -119,8 +119,8 @@ def test_reaper_works_after_pool_discard
# This doesn't test the reaper directly, but we want to test the action
# it would take on a discarded pool
def test_reap_flush_on_discarded_pool
role = duplicated_role
pool = ConnectionPool.new(role)
pool_config = duplicated_pool_config
pool = ConnectionPool.new(pool_config)
pool.discard!
pool.reap
......@@ -128,14 +128,14 @@ def test_reap_flush_on_discarded_pool
end
def test_connection_pool_starts_reaper_in_fork
role = duplicated_role
role.db_config.configuration_hash[:reaping_frequency] = "0.0001"
pool_config = duplicated_pool_config
pool_config.db_config.configuration_hash[:reaping_frequency] = "0.0001"
pool = ConnectionPool.new(role)
pool = ConnectionPool.new(pool_config)
pool.checkout
pid = fork do
pool = ConnectionPool.new(role)
pool = ConnectionPool.new(pool_config)
conn, child = new_conn_in_thread(pool)
child.terminate
......@@ -173,10 +173,10 @@ def test_reaper_does_not_reap_discarded_connection_pools
end
private
def duplicated_role
def duplicated_pool_config
old_config = ActiveRecord::Base.connection_pool.db_config.configuration_hash
db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new("arunit", "primary", old_config.dup)
Role.new("primary", db_config)
PoolConfig.new("primary", db_config)
end
def new_conn_in_thread(pool)
......
......@@ -13,7 +13,7 @@ def setup
@specification = ActiveRecord::Base.remove_connection
# Clear out connection info from other pids (like a fork parent) too
ActiveRecord::ConnectionAdapters::Role.discard_pools!
ActiveRecord::ConnectionAdapters::PoolConfig.discard_pools!
end
teardown do
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册