class ActiveRecord::ConnectionAdapters::ConnectionPool
Базовый класс пула соединений для управления базами данных Active Record.
Введение
Пул соединений синхронизирует доступ потоков к ограниченному числу соединений с базой данных. Основная идея заключается в том, что каждый поток запрашивает соединение из пула, использует его и возвращает в пул. ConnectionPool полностью потокобезопасен и гарантирует, что соединение не может использоваться двумя потоками одновременно, если соблюдаются условия контракта ConnectionPool. Он также обрабатывает случаи, когда количество потоков превышает количество соединений: если все соединения заняты, и поток пытается получить соединение, то ConnectionPool будет ожидать, пока другое соединение не будет возвращено.
Получение (извлечение) соединения
Соединения можно получить и использовать из пула несколькими способами:
-
Просто используйте ActiveRecord::Base.connection, как в Active Record 2.1 и ранее (до пула соединений). Когда вы закончите работу с соединением(ями) и хотите вернуть его в пул, вызывайте ActiveRecord::Base.clear_active_connections!. Это будет поведением по умолчанию для Active Record при использовании в сочетании с обработкой запросов Action Pack.
-
Вручную извлеките соединение из пула с помощью ActiveRecord::Base.connection_pool.checkout. Вы сами отвечаете за возврат этого соединения в пул после завершения работы, вызвав ActiveRecord::Base.connection_pool.checkin(connection).
-
Используйте ActiveRecord::Base.connection_pool.with_connection(&block), который получает соединение, передает его как единственный аргумент блоку и возвращает его в пул после завершения блока.
Соединения в пуле фактически являются объектами AbstractAdapter (или объектами, совместимыми с интерфейсом AbstractAdapter).
Параметры
Существует несколько параметров, связанных с пулом соединений, которые вы можете добавить в конфигурацию подключения к базе данных:
-
pool: число, указывающее размер пула соединений (по умолчанию 5) -
checkout_timeout: количество секунд ожидания соединения перед отказом и выбросом ошибки таймаута (по умолчанию 5 секунд). -
reaping_frequency: частота в секундах для периодического запуска Reaper, который пытается найти и восстановить соединения из «мертвых» потоков, что может произойти, если программист забыл закрыть соединение в конце потока или поток неожиданно завершился. Независимо от этого параметра, Reaper будет вызван перед каждым блокирующим ожиданием. (По умолчанию nil, что означает, что Reaper не будет планироваться).
Атрибуты
Методы публичного класса
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 233 def initialize(spec) super() @spec = spec @checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5 @reaper = Reaper.new(self, (spec.config[:reaping_frequency] && spec.config[:reaping_frequency].to_f)) @reaper.run # default max pool size to 5 @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5 # The cache of reserved connections mapped to threads @reserved_connections = ThreadSafe::Cache.new(:initial_capacity => @size) @connections = [] @automatic_reconnect = true @available = Queue.new self end
Создает новый объект ConnectionPool. spec — это объект ConnectionSpecification, описывающий информацию о подключении к базе данных (например, адаптер, имя хоста, имя пользователя, пароль и т. д.), а также максимальный размер для этого объекта ConnectionPool.
По умолчанию максимальный размер объекта ConnectionPool равен 5.
Методы публичного экземпляра
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 268
def active_connection?
synchronize do
@reserved_connections.fetch(current_connection_id) {
return false
}.in_use?
end
end Существует ли открытое подключение, используемое текущей нитью?
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 360
def checkin(conn)
synchronize do
owner = conn.owner
conn._run_checkin_callbacks do
conn.expire
end
release conn, owner
@available.add conn
end
end Возврат подключения к базе данных в пул, что означает, что вам больше не нужно это подключение.
conn: объект AbstractAdapter, полученный ранее с помощью вызова checkout для этого пула.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 347
def checkout
synchronize do
conn = acquire_connection
conn.lease
checkout_and_verify(conn)
end
end Выдача подключения к базе данных из пула, что означает, что вы хотите его использовать. Вы должны вызвать checkin, когда вам больше не нужно это подключение.
Это выполняется либо путем возврата и предоставления существующего подключения, либо путем создания нового подключения и предоставления его.
Если все подключения выданы, и пул заполнен (т. е. число выданных подключений больше или равно предельному размеру), будет вызвано исключение ActiveRecord::ConnectionTimeoutError.
Возвращает: объект AbstractAdapter.
Возбуждает:
-
ConnectionTimeoutError: подключение не может быть получено из пула.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 316
def clear_reloadable_connections!
synchronize do
@reserved_connections.clear
@connections.each do |conn|
checkin conn
conn.disconnect! if conn.requires_reloading?
end
@connections.delete_if do |conn|
conn.requires_reloading?
end
@available.clear
@connections.each do |conn|
@available.add conn
end
end
end Очищает кэш, который сопоставляет классы.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 298
def connected?
synchronize { @connections.any? }
end Возвращает true, если подключение уже открыто.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 259
def connection
# this is correctly done double-checked locking
# (ThreadSafe::Cache's lookups have volatile semantics)
@reserved_connections[current_connection_id] || synchronize do
@reserved_connections[current_connection_id] ||= checkout
end
end Получение подключения, связанного с текущей нитью, или вызов checkout для получения его, если необходимо.
connection может вызываться любое количество раз; подключение хранится в хэше, ключом которого является идентификатор нити.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 303
def disconnect!
synchronize do
@reserved_connections.clear
@connections.each do |conn|
checkin conn
conn.disconnect!
end
@connections = []
@available.clear
end
end Отключает все подключения в пуле и очищает пул.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 390
def reap
stale_connections = synchronize do
@connections.select do |conn|
conn.in_use? && !conn.owner.alive?
end
end
stale_connections.each do |conn|
synchronize do
if conn.active?
conn.reset!
checkin conn
else
remove conn
end
end
end
end Восстановление потерянных подключений для пула. Потеря подключения может произойти, если программист забывает проверить подключение в конце нити или нить завершается неожиданно.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 279
def release_connection(with_id = current_connection_id)
synchronize do
conn = @reserved_connections.delete(with_id)
checkin conn if conn
end
end Сигнализирует, что нить завершила работу с текущим подключением. release_connection освобождает связь подключение-нить и возвращает подключение в пул.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 376
def remove(conn)
synchronize do
@connections.delete conn
@available.delete conn
release conn, conn.owner
@available.add checkout_new_connection if @available.any_waiting?
end
end Удаление подключения из пула подключений. Подключение останется открытым и активным, но больше не будет управляться этим пулом.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 289 def with_connection connection_id = current_connection_id fresh_connection = true unless active_connection? yield connection ensure release_connection(connection_id) if fresh_connection end
Если подключение уже существует, передайте его в блок. Если подключение не существует, выдайте подключение, передайте его в блок и верните подключение в пул по завершении.
© 2004–2018 David Heinemeier Hansson
Licensed under the MIT License.