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
|
# will not work properly with huge numbers of open connections
|
||||||
# reap connections from pool that are older than 30 seconds
|
# reap connections from pool that are older than 30 seconds
|
||||||
connection_reaper_age = 30
|
connection_reaper_age = 30
|
||||||
|
|
||||||
# run reap check every 30 seconds
|
# run reap check every 30 seconds
|
||||||
connection_reaper_interval = 30
|
connection_reaper_interval = 30
|
||||||
# also reap any connections older than this
|
|
||||||
connection_reaper_max_age = 600
|
|
||||||
|
|
||||||
# set to relative URL (for subdirectory hosting)
|
# set to relative URL (for subdirectory hosting)
|
||||||
# IMPORTANT: path must not include a trailing /
|
# IMPORTANT: path must not include a trailing /
|
||||||
|
|
|
@ -435,7 +435,7 @@ module Discourse
|
||||||
# after fork, otherwise Discourse will be
|
# after fork, otherwise Discourse will be
|
||||||
# in a bad state
|
# in a bad state
|
||||||
def self.after_fork
|
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
|
MessageBus.after_fork
|
||||||
SiteSetting.after_fork
|
SiteSetting.after_fork
|
||||||
$redis._client.reconnect
|
$redis._client.reconnect
|
||||||
|
@ -487,7 +487,7 @@ module Discourse
|
||||||
while true
|
while true
|
||||||
begin
|
begin
|
||||||
sleep GlobalSetting.connection_reaper_interval
|
sleep GlobalSetting.connection_reaper_interval
|
||||||
reap_connections(GlobalSetting.connection_reaper_age, GlobalSetting.connection_reaper_max_age)
|
reap_connections(GlobalSetting.connection_reaper_age)
|
||||||
rescue => e
|
rescue => e
|
||||||
Discourse.warn_exception(e, message: "Error reaping connections")
|
Discourse.warn_exception(e, message: "Error reaping connections")
|
||||||
end
|
end
|
||||||
|
@ -495,15 +495,22 @@ module Discourse
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.reap_connections(idle, max_age)
|
def self.reap_connections(idle)
|
||||||
pools = []
|
pools = []
|
||||||
ObjectSpace.each_object(ActiveRecord::ConnectionAdapters::ConnectionPool) { |pool| pools << pool }
|
ObjectSpace.each_object(ActiveRecord::ConnectionAdapters::ConnectionPool) { |pool| pools << pool }
|
||||||
|
|
||||||
|
i = 0
|
||||||
pools.each do |pool|
|
pools.each do |pool|
|
||||||
|
i += 1
|
||||||
# reap recovers connections that were aborted
|
# reap recovers connections that were aborted
|
||||||
# eg a thread died or a dev forgot to check it in
|
# eg a thread died or a dev forgot to check it in
|
||||||
pool.reap
|
# flush removes idle connections
|
||||||
pool.drain(idle.seconds, max_age.seconds)
|
# 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
|
||||||
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