FEATURE: add connnection reaping based on maximum age

This feature ensures connections to the db are always attempted to be closed
after 600 seconds of idle time.
This commit is contained in:
Sam 2015-10-17 11:29:16 +11:00
parent 5bea933370
commit 805120fc95
3 changed files with 11 additions and 8 deletions

View File

@ -123,6 +123,8 @@ new_version_emails = true
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 /

View File

@ -340,7 +340,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) reap_connections(GlobalSetting.connection_reaper_age, GlobalSetting.connection_reaper_max_age)
rescue => e rescue => e
Discourse.handle_exception(e, {message: "Error reaping connections"}) Discourse.handle_exception(e, {message: "Error reaping connections"})
end end
@ -348,12 +348,12 @@ module Discourse
end end
end end
def self.reap_connections(age) def self.reap_connections(idle, max_age)
pools = [] pools = []
ObjectSpace.each_object(ActiveRecord::ConnectionAdapters::ConnectionPool){|pool| pools << pool} ObjectSpace.each_object(ActiveRecord::ConnectionAdapters::ConnectionPool){|pool| pools << pool}
pools.each do |pool| pools.each do |pool|
pool.drain(age.seconds) pool.drain(idle.seconds, max_age.seconds)
end end
end end

View File

@ -2,11 +2,12 @@
if Rails.version >= "4.2.0" if Rails.version >= "4.2.0"
class ActiveRecord::ConnectionAdapters::AbstractAdapter class ActiveRecord::ConnectionAdapters::AbstractAdapter
module LastUseExtension module LastUseExtension
attr_reader :last_use attr_reader :last_use, :first_use
def initialize(connection, logger = nil, pool = nil) def initialize(connection, logger = nil, pool = nil)
super super
@last_use = false @last_use = false
@first_use = Time.now
end end
def lease def lease
@ -26,11 +27,11 @@ end
class ActiveRecord::ConnectionAdapters::ConnectionPool class ActiveRecord::ConnectionAdapters::ConnectionPool
# drain all idle connections # drain all idle connections
# if idle_time is specified only connections idle for N seconds will be drained # if idle_time is specified only connections idle for N seconds will be drained
def drain(idle_time=nil) def drain(idle_time=nil, max_age=nil)
synchronize do synchronize do
@available.clear @available.clear
@connections.delete_if do |conn| @connections.delete_if do |conn|
try_drain?(conn, idle_time) try_drain?(conn, idle_time, max_age)
end end
@connections.each do |conn| @connections.each do |conn|
@ -42,9 +43,9 @@ class ActiveRecord::ConnectionAdapters::ConnectionPool
private private
def try_drain?(conn, idle_time) def try_drain?(conn, idle_time, max_age)
if !conn.in_use? if !conn.in_use?
if !idle_time || conn.last_use < idle_time.seconds.ago if !idle_time || conn.last_use < idle_time.seconds.ago || (max_age && conn.first_use < max_age.seconds.ago)
conn.disconnect! conn.disconnect!
return true return true
end end