FIX: correct pool reaper
This removes a freedom patch and replaces with a custom reaper thread it also captures an issue where reaper would fail when connections where empty
This commit is contained in:
parent
1d9fdcd9d3
commit
87fabdc2f3
|
@ -143,10 +143,9 @@ new_version_emails = true
|
|||
# will not work properly with huge numbers of open connections
|
||||
# reap connections from pool that are older than 30 seconds
|
||||
connection_reaper_age = 30
|
||||
|
||||
# run reap check every 30 seconds
|
||||
connection_reaper_interval = 30
|
||||
# also reap any connections older than this
|
||||
connection_reaper_max_age = 600
|
||||
|
||||
# set to relative URL (for subdirectory hosting)
|
||||
# IMPORTANT: path must not include a trailing /
|
||||
|
|
|
@ -435,7 +435,7 @@ module Discourse
|
|||
# after fork, otherwise Discourse will be
|
||||
# in a bad state
|
||||
def self.after_fork
|
||||
# note: all this reconnecting may no longer be needed per https://github.com/redis/redis-rb/pull/414
|
||||
# note: some of this reconnecting may no longer be needed per https://github.com/redis/redis-rb/pull/414
|
||||
MessageBus.after_fork
|
||||
SiteSetting.after_fork
|
||||
$redis._client.reconnect
|
||||
|
@ -487,7 +487,7 @@ module Discourse
|
|||
while true
|
||||
begin
|
||||
sleep GlobalSetting.connection_reaper_interval
|
||||
reap_connections(GlobalSetting.connection_reaper_age, GlobalSetting.connection_reaper_max_age)
|
||||
reap_connections(GlobalSetting.connection_reaper_age)
|
||||
rescue => e
|
||||
Discourse.warn_exception(e, message: "Error reaping connections")
|
||||
end
|
||||
|
@ -495,15 +495,22 @@ module Discourse
|
|||
end
|
||||
end
|
||||
|
||||
def self.reap_connections(idle, max_age)
|
||||
def self.reap_connections(idle)
|
||||
pools = []
|
||||
ObjectSpace.each_object(ActiveRecord::ConnectionAdapters::ConnectionPool) { |pool| pools << pool }
|
||||
|
||||
i = 0
|
||||
pools.each do |pool|
|
||||
i += 1
|
||||
# reap recovers connections that were aborted
|
||||
# eg a thread died or a dev forgot to check it in
|
||||
pool.reap
|
||||
pool.drain(idle.seconds, max_age.seconds)
|
||||
# flush removes idle connections
|
||||
# after fork we have "deadpools" so ignore them, they have been discarded
|
||||
# so @connections is set to nil
|
||||
if pool.connections
|
||||
pool.reap
|
||||
pool.flush(idle.seconds)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
class ActiveRecord::ConnectionAdapters::AbstractAdapter
|
||||
module LastUseExtension
|
||||
attr_reader :last_use, :first_use
|
||||
|
||||
def initialize(connection, logger = nil, pool = nil)
|
||||
super
|
||||
@last_use = false
|
||||
@first_use = Time.now
|
||||
end
|
||||
|
||||
def lease
|
||||
@lock.synchronize do
|
||||
unless in_use?
|
||||
@last_use = Time.now
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
prepend LastUseExtension
|
||||
end
|
||||
|
||||
class ActiveRecord::ConnectionAdapters::ConnectionPool
|
||||
# drain all idle connections
|
||||
# if idle_time is specified only connections idle for N seconds will be drained
|
||||
def drain(idle_time = nil, max_age = nil)
|
||||
return if !(@connections && @available)
|
||||
|
||||
idle_connections = synchronize do
|
||||
@connections.select do |conn|
|
||||
!conn.in_use? && ((idle_time && conn.last_use <= idle_time.seconds.ago) || (max_age && conn.first_use < max_age.seconds.ago))
|
||||
end.each do |conn|
|
||||
conn.lease
|
||||
|
||||
@available.delete conn
|
||||
@connections.delete conn
|
||||
end
|
||||
end
|
||||
|
||||
idle_connections.each do |conn|
|
||||
conn.disconnect!
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -1,51 +0,0 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe 'pool drainer' do
|
||||
let(:pool) do
|
||||
ActiveRecord::Base.connection_pool
|
||||
end
|
||||
|
||||
it 'can correctly drain the connection pool' do
|
||||
|
||||
pool.reap
|
||||
pool.drain(0)
|
||||
|
||||
old = pool.connections.length
|
||||
expect(old).to eq(1)
|
||||
|
||||
Thread.new do
|
||||
conn = pool.checkout
|
||||
pool.checkin conn
|
||||
end.join
|
||||
|
||||
expect(pool.connections.length).to eq(old + 1)
|
||||
|
||||
freeze_time 1.second.from_now
|
||||
pool.drain(0)
|
||||
expect(pool.connections.length).to eq(old)
|
||||
end
|
||||
|
||||
it 'can drain with idle time setting' do
|
||||
pool.drain
|
||||
old = pool.connections.length
|
||||
expect(old).to eq(1)
|
||||
|
||||
Thread.new do
|
||||
conn = pool.checkout
|
||||
pool.checkin conn
|
||||
end.join
|
||||
|
||||
expect(pool.connections.length).to eq(old + 1)
|
||||
pool.drain(1.minute)
|
||||
expect(pool.connections.length).to eq(old + 1)
|
||||
|
||||
# make sure we don't corrupt internal state
|
||||
20.times do
|
||||
conn = pool.checkout
|
||||
pool.checkin conn
|
||||
pool.drain
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue