Merge ConnectionSpecification + Role -> Role
In order to move schema_cache off of DatabaseConfiguration we needed to make role accessible on pool. While looking at `ConnectionSpecification`, `Role`, and `DatabaseConfig` John and I noticed that this could be achieved by merging `ConnectionSpecification` and `Role` into one `Role` class. This allows us to eliminate the `spec` concept which is confusing. `spec` is a private method so renaming to `resolve_role` is ok. In the `Role` class we took `name` (renamed to `connection_specification_name` for clarity since it's not a `role` name) and `db_config` from `ConnectionSpecification` and the `pool` methods from `Role` and combined them into one `Role` class. This feels a lot cleaner to us because it clarifies the purposes of the classes/methods/variables, and makes it easier to drop `connection_specification_name` keyed on the parent class in the future. There are a lot of changes in here but the majority of them are find and replace `spec` -> `role`, `spec.name` -> `role.connection_specification_name`, `Resolver#spec` -> `Resolver#resolve_role`. This PR also moves the `schema_cache` from `DatabaseConfig` to the new combined `Role` class. Co-authored-by: John Crepezzi <john.crepezzi@gmail.com>
This commit is contained in:
parent
b305f0e206
commit
eeacc03454
|
@ -9,7 +9,7 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
autoload :Column
|
autoload :Column
|
||||||
autoload :ConnectionSpecification
|
autoload :Role
|
||||||
autoload :Resolver
|
autoload :Resolver
|
||||||
|
|
||||||
autoload_at "active_record/connection_adapters/abstract/schema_definitions" do
|
autoload_at "active_record/connection_adapters/abstract/schema_definitions" do
|
||||||
|
|
|
@ -370,20 +370,21 @@ module ActiveRecord
|
||||||
include ConnectionAdapters::AbstractPool
|
include ConnectionAdapters::AbstractPool
|
||||||
|
|
||||||
attr_accessor :automatic_reconnect, :checkout_timeout
|
attr_accessor :automatic_reconnect, :checkout_timeout
|
||||||
attr_reader :db_config, :size, :reaper
|
attr_reader :db_config, :size, :reaper, :role
|
||||||
|
|
||||||
delegate :schema_cache, :schema_cache=, to: :db_config
|
delegate :schema_cache, :schema_cache=, to: :role
|
||||||
|
|
||||||
# Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
|
# Creates a new ConnectionPool object. +role+ is a Role
|
||||||
# object which describes database connection information (e.g. adapter,
|
# object which describes database connection information (e.g. adapter,
|
||||||
# host name, username, password, etc), as well as the maximum size for
|
# host name, username, password, etc), as well as the maximum size for
|
||||||
# this ConnectionPool.
|
# this ConnectionPool.
|
||||||
#
|
#
|
||||||
# The default ConnectionPool maximum size is 5.
|
# The default ConnectionPool maximum size is 5.
|
||||||
def initialize(db_config)
|
def initialize(role)
|
||||||
super()
|
super()
|
||||||
|
|
||||||
@db_config = db_config
|
@role = role
|
||||||
|
@db_config = role.db_config
|
||||||
|
|
||||||
@checkout_timeout = db_config.checkout_timeout
|
@checkout_timeout = db_config.checkout_timeout
|
||||||
@idle_timeout = db_config.idle_timeout
|
@idle_timeout = db_config.idle_timeout
|
||||||
|
@ -950,60 +951,6 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Role # :nodoc:
|
|
||||||
include Mutex_m
|
|
||||||
|
|
||||||
attr_reader :db_config
|
|
||||||
|
|
||||||
INSTANCES = ObjectSpace::WeakMap.new
|
|
||||||
private_constant :INSTANCES
|
|
||||||
|
|
||||||
class << self
|
|
||||||
def discard_pools!
|
|
||||||
INSTANCES.each_key(&:discard_pool!)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(db_config)
|
|
||||||
super()
|
|
||||||
@db_config = db_config
|
|
||||||
@pool = nil
|
|
||||||
INSTANCES[self] = self
|
|
||||||
end
|
|
||||||
|
|
||||||
def disconnect!
|
|
||||||
ActiveSupport::ForkTracker.check!
|
|
||||||
|
|
||||||
return unless @pool
|
|
||||||
|
|
||||||
synchronize do
|
|
||||||
return unless @pool
|
|
||||||
|
|
||||||
@pool.automatic_reconnect = false
|
|
||||||
@pool.disconnect!
|
|
||||||
end
|
|
||||||
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def pool
|
|
||||||
ActiveSupport::ForkTracker.check!
|
|
||||||
|
|
||||||
@pool || synchronize { @pool ||= ConnectionAdapters::ConnectionPool.new(db_config) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def discard_pool!
|
|
||||||
return unless @pool
|
|
||||||
|
|
||||||
synchronize do
|
|
||||||
return unless @pool
|
|
||||||
|
|
||||||
@pool.discard!
|
|
||||||
@pool = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# ConnectionHandler is a collection of ConnectionPool objects. It is used
|
# ConnectionHandler is a collection of ConnectionPool objects. It is used
|
||||||
# for keeping separate connection pools that connect to different databases.
|
# for keeping separate connection pools that connect to different databases.
|
||||||
#
|
#
|
||||||
|
@ -1056,7 +1003,7 @@ module ActiveRecord
|
||||||
private_constant :FINALIZER
|
private_constant :FINALIZER
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
# These caches are keyed by spec.name (ConnectionSpecification#name).
|
# These caches are keyed by role.connection_specification_name (Role#connection_specification_name).
|
||||||
@owner_to_role = Concurrent::Map.new(initial_capacity: 2)
|
@owner_to_role = Concurrent::Map.new(initial_capacity: 2)
|
||||||
|
|
||||||
# Backup finalizer: if the forked child skipped Kernel#fork the early discard has not occurred
|
# Backup finalizer: if the forked child skipped Kernel#fork the early discard has not occurred
|
||||||
|
@ -1094,21 +1041,21 @@ module ActiveRecord
|
||||||
|
|
||||||
def establish_connection(config)
|
def establish_connection(config)
|
||||||
resolver = Resolver.new(Base.configurations)
|
resolver = Resolver.new(Base.configurations)
|
||||||
spec = resolver.spec(config)
|
role = resolver.resolve_role(config)
|
||||||
db_config = spec.db_config
|
db_config = role.db_config
|
||||||
|
|
||||||
remove_connection(spec.name)
|
remove_connection(role.connection_specification_name)
|
||||||
|
|
||||||
message_bus = ActiveSupport::Notifications.instrumenter
|
message_bus = ActiveSupport::Notifications.instrumenter
|
||||||
payload = {
|
payload = {
|
||||||
connection_id: object_id
|
connection_id: object_id
|
||||||
}
|
}
|
||||||
if spec
|
if role
|
||||||
payload[:spec_name] = spec.name
|
payload[:spec_name] = role.connection_specification_name
|
||||||
payload[:config] = db_config.configuration_hash
|
payload[:config] = db_config.configuration_hash
|
||||||
end
|
end
|
||||||
|
|
||||||
role = owner_to_role[spec.name] = Role.new(db_config)
|
role = owner_to_role[role.connection_specification_name] = Role.new(role.connection_specification_name, db_config)
|
||||||
|
|
||||||
message_bus.instrument("!connection.active_record", payload) do
|
message_bus.instrument("!connection.active_record", payload) do
|
||||||
role.pool
|
role.pool
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require "uri"
|
|
||||||
|
|
||||||
module ActiveRecord
|
|
||||||
module ConnectionAdapters
|
|
||||||
class ConnectionSpecification # :nodoc:
|
|
||||||
attr_reader :name, :db_config
|
|
||||||
|
|
||||||
def initialize(name, db_config)
|
|
||||||
@name, @db_config = name, db_config
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
module ActiveRecord
|
module ActiveRecord
|
||||||
module ConnectionAdapters
|
module ConnectionAdapters
|
||||||
# Builds a ConnectionSpecification from user input.
|
# Builds a Role from user input.
|
||||||
class Resolver # :nodoc:
|
class Resolver # :nodoc:
|
||||||
attr_reader :configurations
|
attr_reader :configurations
|
||||||
|
|
||||||
|
@ -11,17 +11,17 @@ module ActiveRecord
|
||||||
@configurations = configurations
|
@configurations = configurations
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns an instance of ConnectionSpecification for a given adapter.
|
# Returns an instance of Role for a given adapter.
|
||||||
# Accepts a hash one layer deep that contains all connection information.
|
# Accepts a hash one layer deep that contains all connection information.
|
||||||
#
|
#
|
||||||
# == Example
|
# == Example
|
||||||
#
|
#
|
||||||
# config = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
|
# config = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
|
||||||
# spec = Resolver.new(config).spec(:production)
|
# role = Resolver.new(config).resolve_role(:production)
|
||||||
# spec.db_config.configuration_hash
|
# role.db_config.configuration_hash
|
||||||
# # => { host: "localhost", database: "foo", adapter: "sqlite3" }
|
# # => { host: "localhost", database: "foo", adapter: "sqlite3" }
|
||||||
#
|
#
|
||||||
def spec(config)
|
def resolve_role(config)
|
||||||
pool_name = config if config.is_a?(Symbol)
|
pool_name = config if config.is_a?(Symbol)
|
||||||
|
|
||||||
db_config = resolve(config, pool_name)
|
db_config = resolve(config, pool_name)
|
||||||
|
@ -53,7 +53,7 @@ module ActiveRecord
|
||||||
raise AdapterNotFound, "database configuration specifies nonexistent #{db_config.adapter} adapter"
|
raise AdapterNotFound, "database configuration specifies nonexistent #{db_config.adapter} adapter"
|
||||||
end
|
end
|
||||||
|
|
||||||
ConnectionSpecification.new(db_config.configuration_hash.delete(:name) || "primary", db_config)
|
Role.new(db_config.configuration_hash.delete(:name) || "primary", db_config)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns fully resolved connection, accepts hash, string or symbol.
|
# Returns fully resolved connection, accepts hash, string or symbol.
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module ActiveRecord
|
||||||
|
module ConnectionAdapters
|
||||||
|
class Role # :nodoc:
|
||||||
|
include Mutex_m
|
||||||
|
|
||||||
|
attr_reader :db_config, :connection_specification_name
|
||||||
|
attr_accessor :schema_cache
|
||||||
|
|
||||||
|
INSTANCES = ObjectSpace::WeakMap.new
|
||||||
|
private_constant :INSTANCES
|
||||||
|
|
||||||
|
class << self
|
||||||
|
def discard_pools!
|
||||||
|
INSTANCES.each_key(&:discard_pool!)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(connection_specification_name, db_config)
|
||||||
|
super()
|
||||||
|
@connection_specification_name = connection_specification_name
|
||||||
|
@db_config = db_config
|
||||||
|
@pool = nil
|
||||||
|
INSTANCES[self] = self
|
||||||
|
end
|
||||||
|
|
||||||
|
def disconnect!
|
||||||
|
ActiveSupport::ForkTracker.check!
|
||||||
|
|
||||||
|
return unless @pool
|
||||||
|
|
||||||
|
synchronize do
|
||||||
|
return unless @pool
|
||||||
|
|
||||||
|
@pool.automatic_reconnect = false
|
||||||
|
@pool.disconnect!
|
||||||
|
end
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def pool
|
||||||
|
ActiveSupport::ForkTracker.check!
|
||||||
|
|
||||||
|
@pool || synchronize { @pool ||= ConnectionAdapters::ConnectionPool.new(self) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def discard_pool!
|
||||||
|
return unless @pool
|
||||||
|
|
||||||
|
synchronize do
|
||||||
|
return unless @pool
|
||||||
|
|
||||||
|
@pool.discard!
|
||||||
|
@pool = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ActiveSupport::ForkTracker.after_fork { ActiveRecord::ConnectionAdapters::Role.discard_pools! }
|
|
@ -8,8 +8,6 @@ module ActiveRecord
|
||||||
class DatabaseConfig # :nodoc:
|
class DatabaseConfig # :nodoc:
|
||||||
attr_reader :env_name, :spec_name
|
attr_reader :env_name, :spec_name
|
||||||
|
|
||||||
attr_accessor :schema_cache
|
|
||||||
|
|
||||||
def initialize(env_name, spec_name)
|
def initialize(env_name, spec_name)
|
||||||
@env_name = env_name
|
@env_name = env_name
|
||||||
@spec_name = spec_name
|
@spec_name = spec_name
|
||||||
|
@ -61,5 +59,3 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
ActiveSupport::ForkTracker.after_fork { ActiveRecord::ConnectionAdapters::Role.discard_pools! }
|
|
||||||
|
|
|
@ -40,7 +40,8 @@ module ActiveRecord
|
||||||
|
|
||||||
def test_close
|
def test_close
|
||||||
db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new("test", "primary", {})
|
db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new("test", "primary", {})
|
||||||
pool = Pool.new(db_config)
|
role = ActiveRecord::ConnectionAdapters::Role.new("primary", db_config)
|
||||||
|
pool = Pool.new(role)
|
||||||
pool.insert_connection_for_test! @adapter
|
pool.insert_connection_for_test! @adapter
|
||||||
@adapter.pool = pool
|
@adapter.pool = pool
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,9 @@ module ActiveRecord
|
||||||
super
|
super
|
||||||
|
|
||||||
# Keep a duplicate pool so we do not bother others
|
# Keep a duplicate pool so we do not bother others
|
||||||
@pool = ConnectionPool.new(ActiveRecord::Base.connection_pool.db_config)
|
@db_config = ActiveRecord::Base.connection_pool.db_config
|
||||||
|
@role = ActiveRecord::ConnectionAdapters::Role.new("primary", @db_config)
|
||||||
|
@pool = ConnectionPool.new(@role)
|
||||||
|
|
||||||
if in_memory_db?
|
if in_memory_db?
|
||||||
# Separate connections to an in-memory database create an entirely new database,
|
# Separate connections to an in-memory database create an entirely new database,
|
||||||
|
@ -198,9 +200,9 @@ module ActiveRecord
|
||||||
|
|
||||||
def test_idle_timeout_configuration
|
def test_idle_timeout_configuration
|
||||||
@pool.disconnect!
|
@pool.disconnect!
|
||||||
db_config = ActiveRecord::Base.connection_pool.db_config
|
@db_config.configuration_hash.merge!(idle_timeout: "0.02")
|
||||||
db_config.configuration_hash.merge!(idle_timeout: "0.02")
|
role = ActiveRecord::ConnectionAdapters::Role.new("primary", @db_config)
|
||||||
@pool = ConnectionPool.new(db_config)
|
@pool = ConnectionPool.new(role)
|
||||||
idle_conn = @pool.checkout
|
idle_conn = @pool.checkout
|
||||||
@pool.checkin(idle_conn)
|
@pool.checkin(idle_conn)
|
||||||
|
|
||||||
|
@ -223,9 +225,9 @@ module ActiveRecord
|
||||||
|
|
||||||
def test_disable_flush
|
def test_disable_flush
|
||||||
@pool.disconnect!
|
@pool.disconnect!
|
||||||
db_config = ActiveRecord::Base.connection_pool.db_config
|
@db_config.configuration_hash.merge!(idle_timeout: -5)
|
||||||
db_config.configuration_hash.merge!(idle_timeout: -5)
|
role = ActiveRecord::ConnectionAdapters::Role.new("primary", @db_config)
|
||||||
@pool = ConnectionPool.new(db_config)
|
@pool = ConnectionPool.new(role)
|
||||||
idle_conn = @pool.checkout
|
idle_conn = @pool.checkout
|
||||||
@pool.checkin(idle_conn)
|
@pool.checkin(idle_conn)
|
||||||
|
|
||||||
|
@ -315,7 +317,7 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_checkout_behaviour
|
def test_checkout_behaviour
|
||||||
pool = ConnectionPool.new(ActiveRecord::Base.connection_pool.db_config)
|
pool = ConnectionPool.new(@role)
|
||||||
main_connection = pool.connection
|
main_connection = pool.connection
|
||||||
assert_not_nil main_connection
|
assert_not_nil main_connection
|
||||||
threads = []
|
threads = []
|
||||||
|
@ -448,7 +450,7 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_automatic_reconnect_restores_after_disconnect
|
def test_automatic_reconnect_restores_after_disconnect
|
||||||
pool = ConnectionPool.new(ActiveRecord::Base.connection_pool.db_config)
|
pool = ConnectionPool.new(@role)
|
||||||
assert pool.automatic_reconnect
|
assert pool.automatic_reconnect
|
||||||
assert pool.connection
|
assert pool.connection
|
||||||
|
|
||||||
|
@ -457,7 +459,7 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_automatic_reconnect_can_be_disabled
|
def test_automatic_reconnect_can_be_disabled
|
||||||
pool = ConnectionPool.new(ActiveRecord::Base.connection_pool.db_config)
|
pool = ConnectionPool.new(@role)
|
||||||
pool.disconnect!
|
pool.disconnect!
|
||||||
pool.automatic_reconnect = false
|
pool.automatic_reconnect = false
|
||||||
|
|
||||||
|
@ -718,12 +720,13 @@ module ActiveRecord
|
||||||
|
|
||||||
private
|
private
|
||||||
def with_single_connection_pool
|
def with_single_connection_pool
|
||||||
old_config = ActiveRecord::Base.connection_pool.db_config.configuration_hash
|
old_config = @db_config.configuration_hash
|
||||||
db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new("arunit", "primary", old_config.dup)
|
db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new("arunit", "primary", old_config.dup)
|
||||||
|
|
||||||
db_config.configuration_hash[:pool] = 1 # this is safe to do, because .dupped ConnectionSpecification also auto-dups its config
|
db_config.configuration_hash[:pool] = 1 # this is safe to do, because .dupped Role also auto-dups its config
|
||||||
|
|
||||||
yield(pool = ConnectionPool.new(db_config))
|
role = ActiveRecord::ConnectionAdapters::Role.new("primary", db_config)
|
||||||
|
yield(pool = ConnectionPool.new(role))
|
||||||
ensure
|
ensure
|
||||||
pool.disconnect! if pool
|
pool.disconnect! if pool
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,23 +4,23 @@ require "cases/helper"
|
||||||
|
|
||||||
module ActiveRecord
|
module ActiveRecord
|
||||||
module ConnectionAdapters
|
module ConnectionAdapters
|
||||||
class ConnectionSpecification
|
class Role
|
||||||
class ResolverTest < ActiveRecord::TestCase
|
class ResolverTest < ActiveRecord::TestCase
|
||||||
def resolve(spec, config = {})
|
def resolve(role, config = {})
|
||||||
configs = ActiveRecord::DatabaseConfigurations.new(config)
|
configs = ActiveRecord::DatabaseConfigurations.new(config)
|
||||||
resolver = ConnectionAdapters::Resolver.new(configs)
|
resolver = ConnectionAdapters::Resolver.new(configs)
|
||||||
resolver.resolve(spec, spec).configuration_hash
|
resolver.resolve(role, role).configuration_hash
|
||||||
end
|
end
|
||||||
|
|
||||||
def spec(spec, config = {})
|
def resolve_role(role, config = {})
|
||||||
configs = ActiveRecord::DatabaseConfigurations.new(config)
|
configs = ActiveRecord::DatabaseConfigurations.new(config)
|
||||||
resolver = ConnectionAdapters::Resolver.new(configs)
|
resolver = ConnectionAdapters::Resolver.new(configs)
|
||||||
resolver.spec(spec)
|
resolver.resolve_role(role)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_url_invalid_adapter
|
def test_url_invalid_adapter
|
||||||
error = assert_raises(LoadError) do
|
error = assert_raises(LoadError) do
|
||||||
spec "ridiculous://foo?encoding=utf8"
|
resolve_role "ridiculous://foo?encoding=utf8"
|
||||||
end
|
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
|
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 @@ module ActiveRecord
|
||||||
|
|
||||||
def test_error_if_no_adapter_method
|
def test_error_if_no_adapter_method
|
||||||
error = assert_raises(AdapterNotFound) do
|
error = assert_raises(AdapterNotFound) do
|
||||||
spec "abstract://foo?encoding=utf8"
|
resolve_role "abstract://foo?encoding=utf8"
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_match "database configuration specifies nonexistent abstract adapter", error.message
|
assert_match "database configuration specifies nonexistent abstract adapter", error.message
|
||||||
|
@ -38,121 +38,121 @@ module ActiveRecord
|
||||||
# checks that the adapter file can be required in.
|
# checks that the adapter file can be required in.
|
||||||
|
|
||||||
def test_url_from_environment
|
def test_url_from_environment
|
||||||
spec = resolve :production, "production" => "abstract://foo?encoding=utf8"
|
role = resolve :production, "production" => "abstract://foo?encoding=utf8"
|
||||||
assert_equal({
|
assert_equal({
|
||||||
adapter: "abstract",
|
adapter: "abstract",
|
||||||
host: "foo",
|
host: "foo",
|
||||||
encoding: "utf8",
|
encoding: "utf8",
|
||||||
name: "production"
|
name: "production"
|
||||||
}, spec)
|
}, role)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_url_sub_key
|
def test_url_sub_key
|
||||||
spec = resolve :production, "production" => { "url" => "abstract://foo?encoding=utf8" }
|
role = resolve :production, "production" => { "url" => "abstract://foo?encoding=utf8" }
|
||||||
assert_equal({
|
assert_equal({
|
||||||
adapter: "abstract",
|
adapter: "abstract",
|
||||||
host: "foo",
|
host: "foo",
|
||||||
encoding: "utf8",
|
encoding: "utf8",
|
||||||
name: "production"
|
name: "production"
|
||||||
}, spec)
|
}, role)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_url_sub_key_merges_correctly
|
def test_url_sub_key_merges_correctly
|
||||||
hash = { "url" => "abstract://foo?encoding=utf8&", "adapter" => "sqlite3", "host" => "bar", "pool" => "3" }
|
hash = { "url" => "abstract://foo?encoding=utf8&", "adapter" => "sqlite3", "host" => "bar", "pool" => "3" }
|
||||||
spec = resolve :production, "production" => hash
|
role = resolve :production, "production" => hash
|
||||||
assert_equal({
|
assert_equal({
|
||||||
adapter: "abstract",
|
adapter: "abstract",
|
||||||
host: "foo",
|
host: "foo",
|
||||||
encoding: "utf8",
|
encoding: "utf8",
|
||||||
pool: "3",
|
pool: "3",
|
||||||
name: "production"
|
name: "production"
|
||||||
}, spec)
|
}, role)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_url_host_no_db
|
def test_url_host_no_db
|
||||||
spec = resolve "abstract://foo?encoding=utf8"
|
role = resolve "abstract://foo?encoding=utf8"
|
||||||
assert_equal({
|
assert_equal({
|
||||||
adapter: "abstract",
|
adapter: "abstract",
|
||||||
host: "foo",
|
host: "foo",
|
||||||
encoding: "utf8"
|
encoding: "utf8"
|
||||||
}, spec)
|
}, role)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_url_missing_scheme
|
def test_url_missing_scheme
|
||||||
spec = resolve "foo"
|
role = resolve "foo"
|
||||||
assert_equal({ database: "foo" }, spec)
|
assert_equal({ database: "foo" }, role)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_url_host_db
|
def test_url_host_db
|
||||||
spec = resolve "abstract://foo/bar?encoding=utf8"
|
role = resolve "abstract://foo/bar?encoding=utf8"
|
||||||
assert_equal({
|
assert_equal({
|
||||||
adapter: "abstract",
|
adapter: "abstract",
|
||||||
database: "bar",
|
database: "bar",
|
||||||
host: "foo",
|
host: "foo",
|
||||||
encoding: "utf8"
|
encoding: "utf8"
|
||||||
}, spec)
|
}, role)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_url_port
|
def test_url_port
|
||||||
spec = resolve "abstract://foo:123?encoding=utf8"
|
role = resolve "abstract://foo:123?encoding=utf8"
|
||||||
assert_equal({
|
assert_equal({
|
||||||
adapter: "abstract",
|
adapter: "abstract",
|
||||||
port: 123,
|
port: 123,
|
||||||
host: "foo",
|
host: "foo",
|
||||||
encoding: "utf8"
|
encoding: "utf8"
|
||||||
}, spec)
|
}, role)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_encoded_password
|
def test_encoded_password
|
||||||
password = "am@z1ng_p@ssw0rd#!"
|
password = "am@z1ng_p@ssw0rd#!"
|
||||||
encoded_password = URI.encode_www_form_component(password)
|
encoded_password = URI.encode_www_form_component(password)
|
||||||
spec = resolve "abstract://foo:#{encoded_password}@localhost/bar"
|
role = resolve "abstract://foo:#{encoded_password}@localhost/bar"
|
||||||
assert_equal password, spec[:password]
|
assert_equal password, role[:password]
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_url_with_authority_for_sqlite3
|
def test_url_with_authority_for_sqlite3
|
||||||
spec = resolve "sqlite3:///foo_test"
|
role = resolve "sqlite3:///foo_test"
|
||||||
assert_equal("/foo_test", spec[:database])
|
assert_equal("/foo_test", role[:database])
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_url_absolute_path_for_sqlite3
|
def test_url_absolute_path_for_sqlite3
|
||||||
spec = resolve "sqlite3:/foo_test"
|
role = resolve "sqlite3:/foo_test"
|
||||||
assert_equal("/foo_test", spec[:database])
|
assert_equal("/foo_test", role[:database])
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_url_relative_path_for_sqlite3
|
def test_url_relative_path_for_sqlite3
|
||||||
spec = resolve "sqlite3:foo_test"
|
role = resolve "sqlite3:foo_test"
|
||||||
assert_equal("foo_test", spec[:database])
|
assert_equal("foo_test", role[:database])
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_url_memory_db_for_sqlite3
|
def test_url_memory_db_for_sqlite3
|
||||||
spec = resolve "sqlite3::memory:"
|
role = resolve "sqlite3::memory:"
|
||||||
assert_equal(":memory:", spec[:database])
|
assert_equal(":memory:", role[:database])
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_url_sub_key_for_sqlite3
|
def test_url_sub_key_for_sqlite3
|
||||||
spec = resolve :production, "production" => { "url" => "sqlite3:foo?encoding=utf8" }
|
role = resolve :production, "production" => { "url" => "sqlite3:foo?encoding=utf8" }
|
||||||
assert_equal({
|
assert_equal({
|
||||||
adapter: "sqlite3",
|
adapter: "sqlite3",
|
||||||
database: "foo",
|
database: "foo",
|
||||||
encoding: "utf8",
|
encoding: "utf8",
|
||||||
name: "production"
|
name: "production"
|
||||||
}, spec)
|
}, role)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_spec_name_on_key_lookup
|
def test_role_connection_specification_name_on_key_lookup
|
||||||
spec = spec(:readonly, "readonly" => { "adapter" => "sqlite3" })
|
role = resolve_role(:readonly, "readonly" => { "adapter" => "sqlite3" })
|
||||||
assert_equal "readonly", spec.name
|
assert_equal "readonly", role.connection_specification_name
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_spec_name_with_inline_config
|
def test_role_connection_specification_name_with_inline_config
|
||||||
spec = spec("adapter" => "sqlite3")
|
role = resolve_role("adapter" => "sqlite3")
|
||||||
assert_equal "primary", spec.name, "should default to primary id"
|
assert_equal "primary", role.connection_specification_name, "should default to primary id"
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_spec_with_invalid_type
|
def test_role_with_invalid_type
|
||||||
assert_raises TypeError do
|
assert_raises TypeError do
|
||||||
spec(Object.new)
|
resolve_role(Object.new)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,8 +14,8 @@ class TestDisconnectedAdapter < ActiveRecord::TestCase
|
||||||
|
|
||||||
teardown do
|
teardown do
|
||||||
return if in_memory_db?
|
return if in_memory_db?
|
||||||
spec = ActiveRecord::Base.connection_config
|
role = ActiveRecord::Base.connection_config
|
||||||
ActiveRecord::Base.establish_connection(spec)
|
ActiveRecord::Base.establish_connection(role)
|
||||||
end
|
end
|
||||||
|
|
||||||
unless in_memory_db?
|
unless in_memory_db?
|
||||||
|
|
|
@ -1415,7 +1415,7 @@ class MultipleDatabaseFixturesTest < ActiveRecord::TestCase
|
||||||
private
|
private
|
||||||
def with_temporary_connection_pool
|
def with_temporary_connection_pool
|
||||||
role = ActiveRecord::Base.connection_handler.send(:owner_to_role).fetch("primary")
|
role = ActiveRecord::Base.connection_handler.send(:owner_to_role).fetch("primary")
|
||||||
new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new(role.db_config)
|
new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new(role)
|
||||||
|
|
||||||
role.stub(:pool, new_pool) do
|
role.stub(:pool, new_pool) do
|
||||||
yield
|
yield
|
||||||
|
|
|
@ -556,7 +556,7 @@ class QueryCacheTest < ActiveRecord::TestCase
|
||||||
private
|
private
|
||||||
def with_temporary_connection_pool
|
def with_temporary_connection_pool
|
||||||
role = ActiveRecord::Base.connection_handler.send(:owner_to_role).fetch("primary")
|
role = ActiveRecord::Base.connection_handler.send(:owner_to_role).fetch("primary")
|
||||||
new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new(role.db_config)
|
new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new(role)
|
||||||
|
|
||||||
role.stub(:pool, new_pool) do
|
role.stub(:pool, new_pool) do
|
||||||
yield
|
yield
|
||||||
|
|
|
@ -59,8 +59,8 @@ module ActiveRecord
|
||||||
|
|
||||||
def test_pool_has_reaper
|
def test_pool_has_reaper
|
||||||
config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", spec_name: "primary")
|
config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", spec_name: "primary")
|
||||||
spec = ConnectionSpecification.new("primary", config)
|
role = Role.new("primary", config)
|
||||||
pool = ConnectionPool.new(spec.db_config)
|
pool = ConnectionPool.new(role)
|
||||||
|
|
||||||
assert pool.reaper
|
assert pool.reaper
|
||||||
ensure
|
ensure
|
||||||
|
@ -68,10 +68,10 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_reaping_frequency_configuration
|
def test_reaping_frequency_configuration
|
||||||
spec = duplicated_spec
|
role = duplicated_role
|
||||||
spec.db_config.configuration_hash[:reaping_frequency] = "10.01"
|
role.db_config.configuration_hash[:reaping_frequency] = "10.01"
|
||||||
|
|
||||||
pool = ConnectionPool.new(spec.db_config)
|
pool = ConnectionPool.new(role)
|
||||||
|
|
||||||
assert_equal 10.01, pool.reaper.frequency
|
assert_equal 10.01, pool.reaper.frequency
|
||||||
ensure
|
ensure
|
||||||
|
@ -79,10 +79,10 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_connection_pool_starts_reaper
|
def test_connection_pool_starts_reaper
|
||||||
spec = duplicated_spec
|
role = duplicated_role
|
||||||
spec.db_config.configuration_hash[:reaping_frequency] = "0.0001"
|
role.db_config.configuration_hash[:reaping_frequency] = "0.0001"
|
||||||
|
|
||||||
pool = ConnectionPool.new(spec.db_config)
|
pool = ConnectionPool.new(role)
|
||||||
|
|
||||||
conn, child = new_conn_in_thread(pool)
|
conn, child = new_conn_in_thread(pool)
|
||||||
|
|
||||||
|
@ -97,11 +97,11 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_reaper_works_after_pool_discard
|
def test_reaper_works_after_pool_discard
|
||||||
spec = duplicated_spec
|
role = duplicated_role
|
||||||
spec.db_config.configuration_hash[:reaping_frequency] = "0.0001"
|
role.db_config.configuration_hash[:reaping_frequency] = "0.0001"
|
||||||
|
|
||||||
2.times do
|
2.times do
|
||||||
pool = ConnectionPool.new(spec.db_config)
|
pool = ConnectionPool.new(role)
|
||||||
|
|
||||||
conn, child = new_conn_in_thread(pool)
|
conn, child = new_conn_in_thread(pool)
|
||||||
|
|
||||||
|
@ -119,8 +119,8 @@ module ActiveRecord
|
||||||
# This doesn't test the reaper directly, but we want to test the action
|
# This doesn't test the reaper directly, but we want to test the action
|
||||||
# it would take on a discarded pool
|
# it would take on a discarded pool
|
||||||
def test_reap_flush_on_discarded_pool
|
def test_reap_flush_on_discarded_pool
|
||||||
spec = duplicated_spec
|
role = duplicated_role
|
||||||
pool = ConnectionPool.new(spec.db_config)
|
pool = ConnectionPool.new(role)
|
||||||
|
|
||||||
pool.discard!
|
pool.discard!
|
||||||
pool.reap
|
pool.reap
|
||||||
|
@ -128,14 +128,14 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_connection_pool_starts_reaper_in_fork
|
def test_connection_pool_starts_reaper_in_fork
|
||||||
spec = duplicated_spec
|
role = duplicated_role
|
||||||
spec.db_config.configuration_hash[:reaping_frequency] = "0.0001"
|
role.db_config.configuration_hash[:reaping_frequency] = "0.0001"
|
||||||
|
|
||||||
pool = ConnectionPool.new(spec.db_config)
|
pool = ConnectionPool.new(role)
|
||||||
pool.checkout
|
pool.checkout
|
||||||
|
|
||||||
pid = fork do
|
pid = fork do
|
||||||
pool = ConnectionPool.new(spec.db_config)
|
pool = ConnectionPool.new(role)
|
||||||
|
|
||||||
conn, child = new_conn_in_thread(pool)
|
conn, child = new_conn_in_thread(pool)
|
||||||
child.terminate
|
child.terminate
|
||||||
|
@ -173,10 +173,10 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def duplicated_spec
|
def duplicated_role
|
||||||
old_config = ActiveRecord::Base.connection_pool.db_config.configuration_hash
|
old_config = ActiveRecord::Base.connection_pool.db_config.configuration_hash
|
||||||
db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new("arunit", "primary", old_config.dup)
|
db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new("arunit", "primary", old_config.dup)
|
||||||
ConnectionSpecification.new("primary", db_config)
|
Role.new("primary", db_config)
|
||||||
end
|
end
|
||||||
|
|
||||||
def new_conn_in_thread(pool)
|
def new_conn_in_thread(pool)
|
||||||
|
|
Loading…
Reference in New Issue