Spec-Zone.ru › Ruby on Rails 6.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).

  • idle_timeout: количество секунд, в течение которого подключение будет оставаться неиспользуемым в пуле, прежде чем будет автоматически отключено (по умолчанию 300 секунд). Установите значение 0, чтобы подключения хранились неограниченно.

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

Атрибуты

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

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

new(spec) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 370
def initialize(spec)
  super()

  @spec = spec

  @checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
  if @idle_timeout = spec.config.fetch(:idle_timeout, 300)
    @idle_timeout = @idle_timeout.to_f
    @idle_timeout = nil if @idle_timeout <= 0
  end

  # default max pool size to 5
  @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5

  # This variable tracks the cache of threads mapped to reserved connections, with the
  # sole purpose of speeding up the +connection+ method. It is not the authoritative
  # registry of which thread owns which connection. Connection ownership is tracked by
  # the +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 <tt>@thread_cached_conns</tt> 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

  @lock_thread = false

  # +reaping_frequency+ is configurable mostly for historical reasons, but it could
  # also be useful if someone wants a very low +idle_timeout+.
  reaping_frequency = spec.config.fetch(:reaping_frequency, 60)
  @reaper = Reaper.new(self, reaping_frequency && reaping_frequency.to_f)
  @reaper.run
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 439
def active_connection?
  @thread_cached_conns[connection_cache_key(current_thread)]
end

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

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

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

      conn._run_checkin_callbacks do
        conn.expire
      end

      @available.add conn
    end
  end
end

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

conn: объект AbstractAdapter, который был получен ранее вызовом checkout в этом пуле.

checkout(checkout_timeout = @checkout_timeout) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 586
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 544
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 568
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 471
def connected?
  synchronize { @connections.any? }
end

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

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

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

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

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

Возвращает массив, содержащий соединения, которые в данный момент находятся в пуле. Доступ к массиву не требует синхронизации пула, поскольку массив создается заново и не сохраняется пулом.

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

Вызов методов в соединении без права собственности подчиняется гарантиям потокобезопасности базового метода. Многие методы в классах адаптеров соединений по своей природе не являются потокобезопасными.

disconnect(raise_on_acquisition_timeout = true) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 496
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 518
def disconnect!
  disconnect(false)
end

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

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

flush(minimum_idle = @idle_timeout) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 665
def flush(minimum_idle = @idle_timeout)
  return if minimum_idle.nil?

  idle_connections = synchronize do
    @connections.select do |conn|
      !conn.in_use? && conn.seconds_idle >= minimum_idle
    end.each do |conn|
      conn.lease

      @available.delete conn
      @connections.delete conn
    end
  end

  idle_connections.each do |conn|
    conn.disconnect!
  end
end

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

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

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

lock_thread=(lock_thread) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 417
def lock_thread=(lock_thread)
  if lock_thread
    @lock_thread = Thread.current
  else
    @lock_thread = nil
  end
end
reap() Show source
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 643
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 450
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 611
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

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

stat() Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 699
def stat
  synchronize do
    {
      size: size,
      connections: @connections.size,
      busy: @connections.count { |c| c.in_use? && c.owner.alive? },
      dead: @connections.count { |c| c.in_use? && !c.owner.alive? },
      idle: @connections.count { |c| !c.in_use? },
      waiting: num_waiting_in_queue,
      checkout_timeout: checkout_timeout
    }
  end
end

Возвращает статистику использования пула соединений. Пример:

ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
with_connection() { |conn| ... } Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb, line 460
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–2019 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

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