class ActiveRecord::ConnectionAdapters::ConnectionHandler
Обработчик подключений Active Record
ConnectionHandler представляет собой коллекцию объектов ConnectionPool. Он используется для хранения отдельных пулов подключений, которые соединяются с различными базами данных.
Например, предположим, что у вас есть 5 моделей с такой иерархией:
class Author < ActiveRecord::Base end class BankAccount < ActiveRecord::Base end class Book < ActiveRecord::Base establish_connection :library_db end class ScaryBook < Book end class GoodBook < Book end
И файл database.yml, который выглядит так:
development: database: my_application host: localhost library_db: database: library host: some.library.org
Ваша основная база данных в среде разработки — “my_application”, но модель Book подключается к отдельной базе данных под названием “library_db” (она даже может находиться на другом компьютере).
Book, ScaryBook и GoodBook будут использовать один и тот же пул подключений к “library_db”, в то время как Author, BankAccount и любые другие созданные вами модели будут использовать стандартный пул подключений к “my_application”.
Различные пулы подключений управляются единственным экземпляром ConnectionHandler, доступного через ActiveRecord::Base.connection_handler. Все модели Active Record используют этого обработчика для определения пула подключений, который им следует использовать.
Класс ConnectionHandler не связан с моделями Active, так как он не имеет информации о модели. Модели должны передавать имя спецификации подключения в обработчик, чтобы он мог найти соответствующий пул подключений.
Публичные методы класса
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 73 def initialize # These caches are keyed by pool_config.connection_name (PoolConfig#connection_name). @connection_name_to_pool_manager = Concurrent::Map.new(initial_capacity: 2) end
Публичные методы экземпляра
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 154 def active_connections?(role = nil) each_connection_pool(role).any?(&:active_connection?) end
Возвращает true, если существуют активные подключения среди пулов подключений, которые управляет ConnectionHandler.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 161
def clear_active_connections!(role = nil)
each_connection_pool(role).each do |pool|
pool.release_connection
pool.disable_query_cache!
end
end Возвращает все используемые подключение в текущей нити обратно в пул, а также возвращает подключения в пуле, кэшированные нитями, которые больше не активны.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 175 def clear_all_connections!(role = nil) each_connection_pool(role).each(&:disconnect!) end
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 171 def clear_reloadable_connections!(role = nil) each_connection_pool(role).each(&:clear_reloadable_connections!) end
Очищает кэш, который отображает классы.
См. ConnectionPool#clear_reloadable_connections! для подробностей.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 197 def connected?(connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard) pool = retrieve_connection_pool(connection_name, role: role, shard: shard) pool && pool.connected? end
Возвращает true, если подключение, доступное для этого класса, уже открыто.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 92
def connection_pool_list(role = nil)
if role.nil? || role == :all
connection_name_to_pool_manager.values.flat_map { |m| m.pool_configs.map(&:pool) }
else
connection_name_to_pool_manager.values.flat_map { |m| m.pool_configs(role).map(&:pool) }
end
end Возвращает пулы для обработчика подключений и заданного роли. Если :all передаётся, будут возвращены все пулы, принадлежащие обработчику подключений.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 112
def establish_connection(config, owner_name: Base, role: Base.current_role, shard: Base.current_shard, clobber: false)
owner_name = determine_owner_name(owner_name, config)
pool_config = resolve_pool_config(config, owner_name, role, shard)
db_config = pool_config.db_config
pool_manager = set_pool_manager(pool_config.connection_name)
# If there is an existing pool with the same values as the pool_config
# don't remove the connection. Connections should only be removed if we are
# establishing a connection on a class that is already connected to a different
# configuration.
existing_pool_config = pool_manager.get_pool_config(role, shard)
if !clobber && existing_pool_config && existing_pool_config.db_config == db_config
# Update the pool_config's connection class if it differs. This is used
# for ensuring that ActiveRecord::Base and the primary_abstract_class use
# the same pool. Without this granular swapping will not work correctly.
if owner_name.primary_class? && (existing_pool_config.connection_class != owner_name)
existing_pool_config.connection_class = owner_name
end
existing_pool_config.pool
else
disconnect_pool_from_pool_manager(pool_manager, role, shard)
pool_manager.set_pool_config(role, shard, pool_config)
payload = {
connection_name: pool_config.connection_name,
role: role,
shard: shard,
config: db_config.configuration_hash
}
ActiveSupport::Notifications.instrumenter.instrument("!connection.active_record", payload) do
pool_config.pool
end
end
end # File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 182 def flush_idle_connections!(role = nil) each_connection_pool(role).each(&:flush!) end
Отключает все в настоящее время неиспользуемые подключения.
См. ConnectionPool#flush! для подробностей.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 202
def remove_connection_pool(connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
if pool_manager = get_pool_manager(connection_name)
disconnect_pool_from_pool_manager(pool_manager, role, shard)
end
end # File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 211
def retrieve_connection_pool(connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard, strict: false)
pool_manager = get_pool_manager(connection_name)
pool = pool_manager&.get_pool_config(role, shard)&.pool
if strict && !pool
selector = [
("'#{shard}' shard" unless shard == ActiveRecord::Base.default_shard),
("'#{role}' role" unless role == ActiveRecord::Base.default_role),
].compact.join(" and ")
selector = [
(connection_name unless connection_name == "ActiveRecord::Base"),
selector.presence,
].compact.join(" with ")
selector = " for #{selector}" if selector.present?
message = "No database connection defined#{selector}."
raise ConnectionNotDefined.new(message, connection_name: connection_name, shard: shard, role: role)
end
pool
end Получение пула подключений происходит часто, поэтому мы кешируем его в @connection_name_to_pool_manager. Это делает получение пула подключений O(1) после разогрева процесса. При установлении или удалении подключения мы аннулируем кэш.
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.