/
reaper.rb
79 lines (70 loc) · 2.32 KB
/
reaper.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# frozen_string_literal: true
require "thread"
require "weakref"
module ActiveRecord
module ConnectionAdapters
class ConnectionPool
# = Active Record Connection Pool \Reaper
#
# Every +frequency+ seconds, the reaper will call +reap+ and +flush+ on
# +pool+. A reaper instantiated with a zero frequency will never reap
# the connection pool.
#
# Configure the frequency by setting +reaping_frequency+ in your database
# YAML file (default 60 seconds).
class Reaper
attr_reader :pool, :frequency
def initialize(pool, frequency)
@pool = pool
@frequency = frequency
end
@mutex = Mutex.new
@pools = {}
@threads = {}
class << self
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
private
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
@mutex.synchronize do
@pools[frequency].select! do |pool|
pool.weakref_alive? && !pool.discarded?
end
@pools[frequency].each do |p|
p.reap
p.flush
rescue WeakRef::RefError
end
if @pools[frequency].empty?
@pools.delete(frequency)
@threads.delete(frequency)
running = false
end
end
end
end
end
end
def run
return unless frequency && frequency > 0
self.class.register_pool(pool, frequency)
end
end
end
end
end