Spec-Zone.ru › Ruby on Rails 5.0

класс ActiveRecord::ConnectionAdapters::ConnectionPool

Родитель:
Объект
Включённые модули:
ActiveRecord::ConnectionAdapters::QueryCache::ConnectionPoolConfiguration

Базовый класс пула соединений для управления базами данных Active Record.

Введение

Пул соединений синхронизирует доступ потоков к ограниченному числу соединений с базой данных. Основная идея заключается в том, что каждый поток запрашивает соединение с базы данных из пула, использует его и возвращает в пул. ConnectionPool полностью потокобезопасен и гарантирует, что одно соединение не может быть использовано двумя потоками одновременно, если контракт ConnectionPool соблюдается правильно. Он также обрабатывает случаи, когда количество потоков больше, чем количество соединений: если все соединения заняты, и поток пытается получить соединение, ConnectionPool будет ожидать, пока какое-либо другое соединение будет возвращено в пул.

Получение (выдача) соединения

Соединения могут быть получены и использованы из пула несколькими способами:

  1. Просто используйте ActiveRecord::Base.connection, как в Active Record 2.1 и ранее (до пула соединений). В конце концов, когда вы закончите работу с соединением(ями) и хотите вернуть его в пул, вызываете ActiveRecord::Base.clear_active_connections!. Это будет стандартным поведением Active Record, когда он используется совместно с циклом обработки запросов Action Pack.

  2. Вручную запрашивайте соединение из пула с помощью ActiveRecord::Base.connection_pool.checkout. Вы несете ответственность за возврат этого соединения в пул после завершения работы, вызвав ActiveRecord::Base.connection_pool.checkin(connection).

  3. Используйте ActiveRecord::Base.connection_pool.with_connection(&block), который получает соединение, передает его в качестве единственного аргумента в блок и возвращает его в пул после завершения блока.

Соединения в пуле фактически являются объектами AbstractAdapter (или объектами, совместимыми с интерфейсом AbstractAdapter).

Параметры

Существует несколько параметров, связанных с пулом соединений, которые вы можете добавить в конфигурацию соединения с базой данных:

  • pool: число, указывающее размер пула соединений (по умолчанию 5)

  • checkout_timeout: количество секунд, в течение которых следует блокироваться и ожидать соединения, прежде чем отказаться и выбросить ошибку таймаута (по умолчанию 5 секунд).

  • reaping_frequency: частота в секундах для периодического запуска Reaper, который пытается найти и восстановить соединения из мертвых потоков, что может произойти, если программист забывает закрыть соединение в конце потока или поток завершается неожиданно. Независимо от этого параметра, Reaper будет вызван перед каждым блокирующим ожиданием. (По умолчанию nil, что означает, что Reaper не должен планироваться).

Атрибуты

automatic_reconnect[RW]
checkout_timeout[RW]
connections[R]
reaper[R]
schema_cache[RW]
size[R]
spec[R]

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

new(spec) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 321
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 threads mapped to reserved connections, the sole purpose
  # of the cache is to speed-up +connection+ method, it is not the authoritative
  # registry of which thread owns which connection, that is tracked by
  # +connection.owner+ attr on each +connection+ instance.
  # The invariant works like this: if there is mapping of <tt>thread => conn</tt>,
  # then that +thread+ does indeed own that +conn+, however an absence of a such
  # mapping does not mean that the +thread+ doesn't own the said connection, in
  # that case +conn.owner+ attr should be consulted.
  # Access and modification of +@thread_cached_conns+ does not require
  # synchronization.
  @thread_cached_conns = Concurrent::Map.new(:initial_capacity => @size)

  @connections         = []
  @automatic_reconnect = true

  # Connection pool allows for concurrent (outside the main +synchronize+ section)
  # establishment of new connections. This variable tracks the number of threads
  # currently in the process of independently establishing connections to the DB.
  @now_connecting = 0

  @threads_blocking_new_connections = 0

  @available = ConnectionLeasingQueue.new self
end

Создаёт новый объект ConnectionPool. spec — объект ConnectionSpecification, описывающий информацию о соединении с базой данных (например, адаптер, имя хоста, имя пользователя, пароль и т. д.), а также максимальный размер для этого ConnectionPool.

По умолчанию максимальный размер ConnectionPool составляет 5.

Вызывает метод суперкласса ActiveRecord::ConnectionAdapters::QueryCache::ConnectionPoolConfiguration.new

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

active_connection?() Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 372
def active_connection?
  @thread_cached_conns[connection_cache_key(Thread.current)]
end

Существует ли открытое подключение, используемое для текущей нити?

Этот метод работает только с подключениями, полученными через методы connection или with_connection, подключения, полученные через checkout, не будут обнаружены методом active_connection?

checkin(conn) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 498
def checkin(conn)
  synchronize do
    remove_connection_from_thread_cache conn

    conn._run_checkin_callbacks do
      conn.expire
    end

    @available.add conn
  end
end

Возвращение подключения к базе данных в пул, означающее, что это подключение больше не нужно.

conn: объект AbstractAdapter, полученный ранее с помощью вызова checkout для этого пула.

checkout(checkout_timeout = @checkout_timeout) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 489
def checkout(checkout_timeout = @checkout_timeout)
  checkout_and_verify(acquire_connection(checkout_timeout))
end

Выдача подключения к базе данных из пула, означающее, что вы хотите его использовать. Вы должны вызвать checkin, когда вам больше не нужно это подключение.

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

Если все подключения заняты, а пул заполнен (т.е. количество в настоящее время занятых подключений равно или больше установленного лимита размера), будет поднято исключение ActiveRecord::ConnectionTimeoutError.

Возвращает: объект AbstractAdapter.

Исключения:

  • ActiveRecord::ConnectionTimeoutError — подключение не может быть получено из пула.

clear_reloadable_connections(raise_on_acquisition_timeout = true) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 447
def clear_reloadable_connections(raise_on_acquisition_timeout = true)
  with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
    synchronize do
      @connections.each do |conn|
        if conn.in_use?
          conn.steal!
          checkin conn
        end
        conn.disconnect! if conn.requires_reloading?
      end
      @connections.delete_if(&:requires_reloading?)
      @available.clear
    end
  end
end

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

Исключения:

  • ActiveRecord::ExclusiveConnectionTimeoutError — если невозможно получить владение всеми подключениями в пуле в течение заданного времени ожидания (по умолчанию spec.config[:checkout_timeout] * 2 секунд).

clear_reloadable_connections!() Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 471
def clear_reloadable_connections!
  clear_reloadable_connections(false)
end

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

Пул сначала пытается получить владение всеми подключениями. Если это не удается в течение заданного времени ожидания (по умолчанию spec.config[:checkout_timeout] * 2 секунд), пул насильно очищает кэш и перезагружает подключения без учета других нитей, владеющих подключениями.

connected?() Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 404
def connected?
  synchronize { @connections.any? }
end

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

connection() Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 363
def connection
  @thread_cached_conns[connection_cache_key(Thread.current)] ||= checkout
end

Получение подключения, связанного с текущей нитью, или вызов checkout для получения его при необходимости.

Метод connection можно вызывать любое количество раз; подключение хранится в кэше, индексируемом нитью.

disconnect(raise_on_acquisition_timeout = true) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 414
def disconnect(raise_on_acquisition_timeout = true)
  with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
    synchronize do
      @connections.each do |conn|
        if conn.in_use?
          conn.steal!
          checkin conn
        end
        conn.disconnect!
      end
      @connections = []
      @available.clear
    end
  end
end

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

Исключения:

  • ActiveRecord::ExclusiveConnectionTimeoutError — если невозможно получить владение всеми подключениями в пуле в течение заданного времени ожидания (по умолчанию spec.config[:checkout_timeout] * 2 секунд).

disconnect!() Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 436
def disconnect!
  disconnect(false)
end

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

Пул сначала пытается получить владение всеми подключениями. Если это не удается в течение заданного времени ожидания (по умолчанию spec.config[:checkout_timeout] * 2 секунд), пул насильно отключается без учета других нитей, владеющих подключениями.

reap() Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 544
def reap
  stale_connections = synchronize do
    @connections.select do |conn|
      conn.in_use? && !conn.owner.alive?
    end.each do |conn|
      conn.steal!
    end
  end

  stale_connections.each do |conn|
    if conn.active?
      conn.reset!
      checkin conn
    else
      remove conn
    end
  end
end

Восстановление потерянных подключений для пула. Потеря подключения может произойти, если программист забывает закрыть подключение в конце нити или нить завершается неожиданно.

release_connection(owner_thread = Thread.current) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 383
def release_connection(owner_thread = Thread.current)
  if conn = @thread_cached_conns.delete(connection_cache_key(owner_thread))
    checkin conn
  end
end

Сигнализирует о завершении работы нити с текущим подключением. release_connection освобождает ассоциацию подключение-нить и возвращает подключение в пул.

Этот метод работает только с подключениями, полученными через методы connection или with_connection; подключения, полученные через checkout, не будут автоматически освобождены.

remove(conn) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 512
def remove(conn)
  needs_new_connection = false

  synchronize do
    remove_connection_from_thread_cache conn

    @connections.delete conn
    @available.delete conn

    # @available.any_waiting? => true means that prior to removing this
    # conn, the pool was at its max size (@connections.size == @size)
    # this would mean that any threads stuck waiting in the queue wouldn't
    # know they could checkout_new_connection, so let's do it for them.
    # Because condition-wait loop is encapsulated in the Queue class
    # (that in turn is oblivious to ConnectionPool implementation), threads
    # that are "stuck" there are helpless, they have no way of creating
    # new connections and are completely reliant on us feeding available
    # connections into the Queue.
    needs_new_connection = @available.any_waiting?
  end

  # This is intentionally done outside of the synchronized section as we
  # would like not to hold the main mutex while checking out new connections,
  # thus there is some chance that needs_new_connection information is now
  # stale, we can live with that (bulk_make_new_connections will make
  # sure not to exceed the pool's @size limit).
  bulk_make_new_connections(1) if needs_new_connection
end

Удаление подключения из пула подключений. Подключение останется открытым и активным, но больше не будет управляться этим пулом.

with_connection() { |conn| ... } Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 393
def with_connection
  unless conn = @thread_cached_conns[connection_cache_key(Thread.current)]
    conn = connection
    fresh_connection = true
  end
  yield conn
ensure
  release_connection if fresh_connection
end

Если подключение, полученное через методы connection или with_connection, уже существует, передать его в блок. Если такое подключение не существует, взять подключение, передать его в блок и закрыть подключение по завершении.

© 2004–2018 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

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