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

Inherits:
Object
  • Object
show all
Defined in:
lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb

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 for too long
  • creating occasional activity on inactive 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).

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(pool, frequency) ⇒ Reaper

Returns a new instance of Reaper.



36
37
38
39
# File 'lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb', line 36

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

Instance Attribute Details

#frequency ⇒ Object (readonly)

Returns the value of attribute frequency.



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

def frequency
  @frequency
end

#pool ⇒ Object (readonly)

Returns the value of attribute pool.



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

def pool
  @pool
end

Class Method Details

.discard_pool(pool) ⇒ Object

:nodoc:



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb', line 56

def discard_pool(pool) # :nodoc:
  @mutex.synchronize do
    @pools.each do |frequency, refs|
      refs.reject! do |ref|
        ref.__getobj__ == pool
      rescue WeakRef::RefError
        true
      end

      if refs.empty?
        @pools.delete(frequency)
        @threads.delete(frequency)&.tap { |t| t.kill; t.join }
      end
    end
  end
end

.pools(refs = nil) ⇒ Object

:nodoc:



73
74
75
76
77
78
79
80
# File 'lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb', line 73

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) ⇒ Object

:nodoc:



46
47
48
49
50
51
52
53
54
# File '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

Instance Method Details

#run ⇒ Object



127
128
129
130
# File 'lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb', line 127

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