Spec-Zone.ru › Ruby on Rails 8.1

class ActiveRecord::ConnectionAdapters::ConnectionHandler

Родительский класс:
Object

Обработчик подключений 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 Record, поскольку он не имеет сведений о модели. Чтобы найти нужный пул подключений, модель должна передать обработчику имя спецификации подключения.

Публичные методы класса

new () Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 76
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

Публичные методы экземпляра

active_connections? (role = nil) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 157
def active_connections?(role = nil)
  each_connection_pool(role).any?(&:active_connection?)
end

Возвращает true, если среди пулов подключений, которыми управляет ConnectionHandler, есть активные подключения.

clear_active_connections! (role = nil) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 162
def clear_active_connections!(role = nil)
  each_connection_pool(role).each do |pool|
    pool.release_connection
    pool.disable_query_cache!
  end
end

Возвращает пулу все подключения, используемые текущим потоком.

clear_all_connections! (role = nil) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 176
def clear_all_connections!(role = nil)
  each_connection_pool(role).each(&:disconnect!)
end
clear_reloadable_connections! (role = nil) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 172
def clear_reloadable_connections!(role = nil)
  each_connection_pool(role).each(&:clear_reloadable_connections!)
end

Очищает кэши перезагружаемых подключений во всех пулах подключений.

Подробнее см. в описании ConnectionPool#clear_reloadable_connections!.

connected? (connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 198
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, если подключение, доступное этому классу, уже открыто.

connection_pool_list (role = nil) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 95
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, будут возвращены все пулы, принадлежащие обработчику подключений.

Также имеет псевдоним: connection_pools
connection_pools (role = nil)
Псевдоним для: connection_pool_list
establish_connection (config, owner_name: Base, role: Base.current_role, shard: Base.current_shard, clobber: false) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 115
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_descriptor)

  # 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_descriptor != owner_name)
      existing_pool_config.connection_descriptor = 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_descriptor.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
flush_idle_connections! (role = nil) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 183
def flush_idle_connections!(role = nil)
  each_connection_pool(role).each(&:flush!)
end

Отключает все текущие неактивные подключения.

Подробнее см. в описании ConnectionPool#flush!.

remove_connection_pool (connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 203
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
retrieve_connection_pool (connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard, strict: false) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb, line 212
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.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API