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, который пытается найти и закрыть мертвые соединения, что может произойти, если программист забывает закрыть соединение в конце потока или поток завершается неожиданно. (По умолчанию nil, что означает, что Reaper не запускается). -
dead_connection_timeout: количество секунд с последнего получения соединения, после которого Reaper будет считать соединение подлежащим очистке. (По умолчанию 5 секунд).
Атрибуты
Методы публичного класса
Создаёт новый объект ConnectionPool. spec — это объект ConnectionSpecification, описывающий информацию о подключении к базе данных (например, адаптер, имя хоста, имя пользователя, пароль и т. д.), а также максимальный размер для данного ConnectionPool.
По умолчанию максимальный размер ConnectionPool равен 5.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 234 def initialize(spec) super() @spec = spec @checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5 @dead_connection_timeout = (spec.config[:dead_connection_timeout] && spec.config[:dead_connection_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
Методы публичного экземпляра
Существует ли открытое подключение, используемое для текущей нити?
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 270
def active_connection?
synchronize do
@reserved_connections.fetch(current_connection_id) {
return false
}.in_use?
end
end Возвращает подключение к базе данных в пул, указывая, что оно больше не нужно.
conn: объект AbstractAdapter, полученный ранее путём вызова checkout для этого пула.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 362
def checkin(conn)
synchronize do
conn.run_callbacks :checkin do
conn.expire
end
release conn
@available.add conn
end
end Выдаёт подключение к базе данных из пула, указывая, что вы хотите его использовать. Вы должны вызвать checkin, когда оно больше не нужно.
Это делается либо путём возвращения и аренды существующего подключения, либо путём создания нового подключения и его аренды.
Если все подключения заняты, и пул заполнен (что означает, что количество подключений, находящихся в аренде, больше или равно пределу размера, установленному), будет выброшено исключение ActiveRecord::ConnectionTimeoutError.
Возвращает: объект AbstractAdapter.
Возможные исключения:
-
ConnectionTimeoutError: подключение не может быть получено из пула.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 349
def checkout
synchronize do
conn = acquire_connection
conn.lease
checkout_and_verify(conn)
end
end Очищает кэш, который отображает классы.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 318
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 Возвращает true, если подключение уже открыто.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 300
def connected?
synchronize { @connections.any? }
end Возвращает подключение, связанное с текущей нитью, или вызывает checkout для получения его, если необходимо.
connection можно вызывать любое количество раз; подключение хранится в хэше, ключом которого является идентификатор нити.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 261
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 Отключает все подключения в пуле и очищает его.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 305
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 392
def reap
synchronize do
stale = Time.now - @dead_connection_timeout
connections.dup.each do |conn|
if conn.in_use? && stale > conn.last_use && !conn.active_threadsafe?
remove conn
end
end
end
end Указывает, что нить завершила работу с текущим подключением. release_connection освобождает ассоциацию подключения-нити и возвращает подключение в пул.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 281
def release_connection(with_id = current_connection_id)
synchronize do
conn = @reserved_connections.delete(with_id)
checkin conn if conn
end
end Удаляет подключение из пула подключений. Подключение останется открытым и активным, но больше не будет управляться этим пулом.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 376
def remove(conn)
synchronize do
@connections.delete conn
@available.delete conn
# FIXME: we might want to store the key on the connection so that removing
# from the reserved hash will be a little easier.
release conn
@available.add checkout_new_connection if @available.any_waiting?
end
end Если подключение уже существует, передайте его в блок. Если подключения нет, выдайте подключение, передайте его в блок и верните подключение в пул при завершении.
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 291 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–2016 David Heinemeier Hansson
Licensed under the MIT License.