123456789_123456789_123456789_123456789_123456789_

Class: ActiveRecord::ConnectionAdapters::ConnectionPool::Reaper

Overview

Active Record Connection Pool Reaper

The reaper is a singleton that exists in the background of the process and is responsible for general maintenance of all the connection pools.

It will reclaim connections that are leased to now-dead threads, ensuring that a bad thread can’t leak a pool slot forever. By definition, this involves touching currently-leased connections, but that is safe because the owning thread is known to be dead.

Beyond that, it manages the health of available / unleased connections:

* retiring connections that have been idle[1] for too long
* creating occasional activity on inactive[1] connections
* keeping the pool prepopulated up to its minimum size
* proactively connecting to the target database from any pooled
  connections that had lazily deferred that step
* resetting or replacing connections that are known to be broken

[1]: “idle” and “inactive” here distinguish between connections that have not been requested by the application in a while (idle) and those that have not spoken to their remote server in a while (inactive). The former is a desirable opportunity to reduce our connection count (idle_timeout); the latter is a risk that the server or a firewall may drop a connection we still anticipate using (avoided by #keepalive).

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(pool, frequency) ⇒ Reaper

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb', line 36

def initialize(pool, frequency)
  @pool      = pool
  @frequency = frequency
end

Class Method Details

.pools(refs = nil)

This method is for internal use only.
[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb', line 56

def pools(refs = nil) # :nodoc:
  refs ||= @mutex.synchronize { @pools.values.flatten(1) }

  refs.filter_map do |ref|
    ref.__getobj__ if ref.weakref_alive?
  rescue WeakRef::RefError
  end.select(&:maintainable?)
end

.register_pool(pool, frequency)

This method is for internal use only.
[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb', line 46

def register_pool(pool, frequency) # :nodoc:
  @mutex.synchronize do
    unless @threads[frequency]&.alive?
      @threads[frequency] = spawn_thread(frequency)
    end
    @pools[frequency] ||= []
    @pools[frequency] << WeakRef.new(pool)
  end
end

.spawn_thread(frequency) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb', line 66

def spawn_thread(frequency)
  Thread.new(frequency) do |t|
    # Advise multi-threaded app servers to ignore this thread for
    # the purposes of fork safety warnings
    Thread.current.thread_variable_set(:fork_safe, true)
    Thread.current.name = "AR Pool Reaper"
    running = true
    while running
      sleep t

      refs = nil

      @mutex.synchronize do
        refs = @pools[frequency]

        refs.select! do |pool|
          pool.weakref_alive? && !pool.discarded?
        rescue WeakRef::RefError
        end

        if refs.empty?
          @pools.delete(frequency)
          @threads.delete(frequency)
          running = false
        end
      end

      if running
        pools(refs).each do |pool|
          pool.reaper_lock do
            pool.reap
            pool.flush
            pool.prepopulate
            pool.retire_old_connections
            pool.keep_alive
            pool.preconnect
          end
        end
      end
    end
  end
end

Instance Attribute Details

#frequency (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb', line 34

attr_reader :pool, :frequency

#pool (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb', line 34

attr_reader :pool, :frequency

Instance Method Details

#run

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb', line 110

def run
  return unless frequency && frequency > 0
  self.class.register_pool(pool, frequency)
end