advanced_connection 0.5.14 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +1 -12
- data/lib/advanced_connection/active_record_ext.rb +3 -12
- data/lib/advanced_connection/active_record_ext/abstract_adapter.rb +17 -8
- data/lib/advanced_connection/active_record_ext/connection_pool/idle_manager.rb +113 -168
- data/lib/advanced_connection/active_record_ext/connection_pool/queues.rb +5 -0
- data/lib/advanced_connection/active_record_ext/connection_pool/without_connection.rb +24 -12
- data/lib/advanced_connection/config.rb +26 -15
- data/lib/advanced_connection/error.rb +1 -0
- data/lib/advanced_connection/railtie.rb +0 -6
- data/lib/advanced_connection/version.rb +2 -2
- data/lib/generators/advanced_connection/install/templates/advanced_connection.rb +12 -21
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9dd080219f299367a2de7b23c0e7ce0d83bc20d3
|
4
|
+
data.tar.gz: d3d6b6cca8b0fc5bd717843f00ac5eea6a922589
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df32536d7ff51cea3a0620211290ebec729ecb3c3bc21c6845cea8d2baf8ca300dcb296f6fb3c2704eb14bebc6515a3a305d0df10a1baef19bc48e5ebec8cbfc
|
7
|
+
data.tar.gz: dc871c1ee46031e645cfd0417a6c6261dcf708947cab05b7c400007cbdcf1f298eba9edf8ba97e6a188bc3e6c8fe3f398720d42e41d7f662fc89e19e3da9b788
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -34,8 +34,7 @@ bundle exec rails generate advanced_connection:install
|
|
34
34
|
#### Idle Connection Manager
|
35
35
|
|
36
36
|
Enabling this will enable idle connection management. This allows you to specify settings
|
37
|
-
to enable automatic
|
38
|
-
idle connection culling.
|
37
|
+
to enable automatic idle connection reaping.
|
39
38
|
|
40
39
|
```text
|
41
40
|
enable_idle_connection_manager = true | false
|
@@ -61,16 +60,6 @@ Pool queue type determines both how free connections will be checkout out of the
|
|
61
60
|
connection_pool_queue_type = :fifo | :lifo | :stack | :prefer_older | :prefer_younger
|
62
61
|
```
|
63
62
|
|
64
|
-
How many connections to prestart on initial startup of rails. This can help to reduce the time it takes a restarted production node to start responding again.
|
65
|
-
```text
|
66
|
-
warmup_connections = integer | false
|
67
|
-
```
|
68
|
-
|
69
|
-
Minimum number of connection to keep idle. If, during the idle check, you have fewer than this many connections idle, then a number of new connections will be created up to this this number.
|
70
|
-
```text
|
71
|
-
min_idle_connections = integer
|
72
|
-
```
|
73
|
-
|
74
63
|
Maximum number of connections that can remain idle without being culled. If you have
|
75
64
|
more idle conections than this, only the difference between the total idle and this
|
76
65
|
maximum will be culled.
|
@@ -38,21 +38,12 @@ module AdvancedConnection
|
|
38
38
|
end
|
39
39
|
|
40
40
|
ActiveRecord::ConnectionAdapters::ConnectionPool.instance_exec do
|
41
|
-
if AdvancedConnection.enable_idle_connection_manager
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
if AdvancedConnection.enable_statement_pooling
|
46
|
-
include ConnectionPool::StatementPooling
|
47
|
-
elsif AdvancedConnection.enable_without_connection
|
48
|
-
include ConnectionPool::WithoutConnection
|
49
|
-
end
|
41
|
+
include ConnectionPool::IdleManager if AdvancedConnection.enable_idle_connection_manager
|
42
|
+
include ConnectionPool::WithoutConnection
|
50
43
|
end
|
51
44
|
|
52
45
|
ActiveRecord::Base.instance_exec do
|
53
|
-
|
54
|
-
extend ActiveRecordExt::WithoutConnection unless AdvancedConnection.enable_statement_pooling
|
55
|
-
end
|
46
|
+
extend ActiveRecordExt::WithoutConnection
|
56
47
|
end
|
57
48
|
end
|
58
49
|
end
|
@@ -31,16 +31,25 @@ module AdvancedConnection::ActiveRecordExt
|
|
31
31
|
end
|
32
32
|
|
33
33
|
included do
|
34
|
-
attr_accessor :last_checked_in, :instantiated_at
|
34
|
+
attr_accessor :last_checked_in, :last_checked_out, :instantiated_at
|
35
35
|
alias_method_chain :initialize, :advanced_connection
|
36
|
+
alias_method_chain :lease, :advanced_connection
|
36
37
|
end
|
37
38
|
|
38
39
|
def initialize_with_advanced_connection(*args, &block)
|
39
|
-
@
|
40
|
-
@
|
40
|
+
@instantiated_at = Time.now
|
41
|
+
@last_checked_in = @instantiated_at
|
42
|
+
@last_checked_out = false
|
41
43
|
initialize_without_advanced_connection(*args, &block)
|
42
44
|
end
|
43
45
|
|
46
|
+
def lease_with_advanced_connection
|
47
|
+
synchronize {
|
48
|
+
@last_checked_out = Time.now
|
49
|
+
lease_without_advanced_connection
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
44
53
|
def instance_age
|
45
54
|
(Time.now - @instantiated_at).to_f
|
46
55
|
end
|
@@ -57,20 +66,20 @@ module AdvancedConnection::ActiveRecordExt
|
|
57
66
|
case pool.queue_type
|
58
67
|
when :prefer_younger then
|
59
68
|
# when prefering younger, we sort oldest->youngest
|
60
|
-
#
|
69
|
+
# to ensure that older connections will be reaped
|
61
70
|
# during #remove_idle_connections()
|
62
71
|
-(instance_age <=> other.instance_age)
|
63
72
|
when :prefer_older then
|
64
|
-
# when prefering older, we sort youngest->oldest
|
65
|
-
#
|
73
|
+
# when prefering older, we sort youngest->oldest to
|
74
|
+
# ensure that younger connections will be reaped
|
66
75
|
# during #remove_idle_connections()
|
67
76
|
(instance_age <=> other.instance_age)
|
68
77
|
else
|
69
|
-
#
|
78
|
+
# With fifo / lifo queues, we only care about the
|
70
79
|
# last time a given connection was used (inferred
|
71
80
|
# by when it was last checked into the pool).
|
72
81
|
# This ensures that the longer idling connections
|
73
|
-
# will be
|
82
|
+
# will be reaped.
|
74
83
|
-(last_checked_in <=> other.last_checked_in)
|
75
84
|
end
|
76
85
|
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
# Copyright (C) 2016 Finalsite, LLC
|
3
2
|
# Copyright (C) 2016 Carl P. Corliss <carl.corliss@finalsite.com>
|
4
3
|
#
|
@@ -32,95 +31,99 @@ module AdvancedConnection::ActiveRecordExt
|
|
32
31
|
|
33
32
|
class IdleManager
|
34
33
|
attr_accessor :interval
|
35
|
-
attr_reader :
|
36
|
-
private :thread
|
34
|
+
attr_reader :logger
|
37
35
|
|
38
36
|
def initialize(pool, interval)
|
39
37
|
@pool = pool
|
40
38
|
@interval = interval.to_i
|
41
|
-
@thread = nil
|
42
39
|
@logger = ActiveSupport::Logger.new(Rails.root.join('log', 'idle_manager.log'))
|
43
|
-
@logger.level = Rails.logger.level
|
44
|
-
end
|
45
40
|
|
46
|
-
|
47
|
-
@logger.info(format("#{idle_stats} #{format}", *args))
|
41
|
+
@logger.level = AdvancedConnection.idle_manager_log_level || ::Logger::INFO
|
48
42
|
end
|
49
43
|
|
50
|
-
|
51
|
-
|
44
|
+
%w[debug info warn error].each do |level|
|
45
|
+
define_method("log_#{level}") do |fmt, *args|
|
46
|
+
@logger.send(level, format("[#{Time.now}] #{idle_stats} #{level.upcase}: #{fmt}", *args))
|
47
|
+
end
|
52
48
|
end
|
53
49
|
|
54
|
-
def
|
55
|
-
@
|
50
|
+
def reserved_connections
|
51
|
+
reserved = @pool.instance_variable_get(:@reserved_connections).dup
|
52
|
+
Hash[reserved.keys.zip(reserved.values)]
|
56
53
|
end
|
57
54
|
|
58
|
-
def
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
55
|
+
def dump_connections
|
56
|
+
return unless (stats_dump = Rails.root.join('tmp', 'dump-connections.txt')).exist?
|
57
|
+
|
58
|
+
log_info "Dumping connections"
|
59
|
+
|
60
|
+
id_size = object_id.to_s.size
|
61
|
+
@logger.info(format("%3s: %#{id_size}s\t%9s\t%9s\t%4s\t%s", 'IDX', 'OID', 'AGE', 'IDLE', 'ACTV', 'OWNER'))
|
62
|
+
|
63
|
+
@pool.connections.dup.each_with_index do |connection, index|
|
64
|
+
if connection.in_use?
|
65
|
+
thread_id = reserved_connections.index(connection) || 0
|
66
|
+
thread_hexid = "0x" << (thread_id << 1).to_s(16)
|
66
67
|
end
|
67
|
-
|
68
|
+
|
69
|
+
@logger.info(format("%3d: %#{id_size}d\t%9d\t%9d\t%4s\t%s",
|
70
|
+
index, connection.object_id,
|
71
|
+
connection.instance_age, connection.idle_time,
|
72
|
+
connection.in_use?.to_s, thread_hexid))
|
68
73
|
end
|
74
|
+
|
75
|
+
!!(stats_dump.unlink rescue true) # rubocop:disable Style/RescueModifier
|
69
76
|
end
|
70
77
|
|
71
78
|
def idle_stats
|
72
79
|
stats = @pool.pool_statistics
|
73
|
-
format(
|
74
|
-
|
75
|
-
stats.active, stats.available, stats.idle, stats.total,
|
76
|
-
)
|
80
|
+
format("[Act: %d / Avail: %d (%d idle) / Total: %d]",
|
81
|
+
stats.active, stats.available, stats.idle, stats.total)
|
77
82
|
end
|
78
83
|
|
79
|
-
def
|
80
|
-
|
81
|
-
@thread.alive? ? :running : :dead
|
82
|
-
else
|
83
|
-
:stopped
|
84
|
-
end
|
85
|
-
end
|
84
|
+
def safe_timed_run(last_run = nil)
|
85
|
+
return unless block_given?
|
86
86
|
|
87
|
-
|
88
|
-
stop.start
|
89
|
-
end
|
87
|
+
log_debug "last run was #{Time.now - last_run} seconds ago" if last_run
|
90
88
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
89
|
+
begin
|
90
|
+
start = Time.now
|
91
|
+
yield
|
92
|
+
rescue StandardError => e
|
93
|
+
log_error "#{e.class.name}: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
94
|
+
ensure
|
95
|
+
finish = ((last_run = Time.now) - start).round(6)
|
96
|
+
log_info("finished idle connection tasks in #{finish} seconds")
|
97
|
+
end
|
98
|
+
|
99
|
+
last_run
|
95
100
|
end
|
96
101
|
|
97
|
-
def
|
102
|
+
def run
|
98
103
|
return unless @interval > 0
|
99
104
|
|
100
|
-
|
105
|
+
Thread.new(@pool, @interval) do |pool, interval|
|
106
|
+
Thread.current.name = self.class.name if Thread.current.respond_to? :name=
|
107
|
+
|
108
|
+
begin
|
109
|
+
pool.release_connection if pool.active_connection?
|
110
|
+
rescue StandardError => e
|
111
|
+
log_error "#{e.class.name}: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
112
|
+
end
|
113
|
+
|
101
114
|
log_info("starting idle manager; running every #{interval} seconds")
|
115
|
+
last_run = nil
|
102
116
|
|
103
|
-
loop
|
117
|
+
loop {
|
104
118
|
sleep interval
|
105
119
|
|
106
|
-
|
107
|
-
|
108
|
-
dump_stats
|
109
|
-
|
110
|
-
log_debug("beginning idle connection cleanup")
|
120
|
+
last_run = safe_timed_run(last_run) do
|
121
|
+
dump_connections
|
111
122
|
pool.remove_idle_connections
|
112
|
-
|
113
|
-
log_debug("beginning idle connection warmup")
|
114
|
-
pool.create_idle_connections
|
115
|
-
|
116
|
-
finish = (Time.now - start).round(6)
|
117
|
-
log_info("finished idle connection tasks in #{finish} seconds; next run in #{interval} seconds")
|
118
|
-
rescue => e
|
119
|
-
Rails.logger.error "#{e.class.name}: #{e.message}"
|
120
|
-
e.backtrace.each { |line| Rails.logger.error line }
|
121
123
|
end
|
122
|
-
|
123
|
-
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
124
127
|
self
|
125
128
|
end
|
126
129
|
end
|
@@ -139,41 +142,19 @@ module AdvancedConnection::ActiveRecordExt
|
|
139
142
|
Queues::FIFO.new
|
140
143
|
end
|
141
144
|
|
142
|
-
@idle_manager = IdleManager.new(self, idle_check_interval).tap(&:
|
145
|
+
@idle_manager = IdleManager.new(self, idle_check_interval).tap(&:run)
|
143
146
|
end
|
144
147
|
|
145
|
-
#
|
146
|
-
## SETINGS
|
147
|
-
#
|
148
|
-
|
149
148
|
def queue_type
|
150
|
-
@queue_type ||= spec.config.fetch(
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
def warmup_connection_count
|
155
|
-
@warmup_connection_count ||= begin
|
156
|
-
conns = spec.config[:warmup_connections] || AdvancedConnection.warmup_connections
|
157
|
-
conns.to_i > connection_limit ? connection_limit : conns.to_i
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
def max_idle_time
|
162
|
-
@max_idle_time ||= (spec.config[:max_idle_time] || \
|
163
|
-
AdvancedConnection.max_idle_time).to_i
|
164
|
-
end
|
165
|
-
|
166
|
-
def idle_check_interval
|
167
|
-
@idle_check_interval ||= (spec.config[:idle_check_interval] || \
|
168
|
-
AdvancedConnection.idle_check_interval || \
|
169
|
-
max_idle_time).to_i
|
149
|
+
@queue_type ||= spec.config.fetch(
|
150
|
+
:queue_type, AdvancedConnection.connection_pool_queue_type
|
151
|
+
).to_s.downcase.to_sym
|
170
152
|
end
|
171
153
|
|
172
154
|
def max_idle_connections
|
173
155
|
@max_idle_connections ||= begin
|
174
156
|
begin
|
175
|
-
|
176
|
-
AdvancedConnection.max_idle_connections).to_i
|
157
|
+
spec.config.fetch(:max_idle_connections, AdvancedConnection.max_idle_connections).to_i
|
177
158
|
rescue FloatDomainError => e
|
178
159
|
raise unless e.message =~ /infinity/i
|
179
160
|
::Float::INFINITY
|
@@ -181,120 +162,84 @@ module AdvancedConnection::ActiveRecordExt
|
|
181
162
|
end
|
182
163
|
end
|
183
164
|
|
184
|
-
def
|
185
|
-
@
|
186
|
-
|
187
|
-
min_idle = (min_idle > 0 ? min_idle : 0)
|
188
|
-
min_idle <= max_idle_connections ? min_idle : max_idle_connections
|
165
|
+
def max_idle_time
|
166
|
+
@max_idle_time ||= begin
|
167
|
+
spec.config.fetch(:max_idle_time, AdvancedConnection.max_idle_time).to_i
|
189
168
|
end
|
190
169
|
end
|
191
170
|
|
192
|
-
def
|
193
|
-
@
|
171
|
+
def idle_check_interval
|
172
|
+
@idle_check_interval ||= begin
|
173
|
+
spec.config[:idle_check_interval] || \
|
174
|
+
AdvancedConnection.idle_check_interval || \
|
175
|
+
max_idle_time
|
176
|
+
end.to_i
|
194
177
|
end
|
195
178
|
|
196
179
|
def checkin_with_last_checked_in(conn)
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
total = @connections.size
|
209
|
-
idle = @connections.count(&:idle?)
|
210
|
-
active = @connections.count(&:in_use?)
|
211
|
-
available = total - active
|
212
|
-
|
213
|
-
ActiveSupport::OrderedOptions.new.merge(
|
214
|
-
total: total,
|
215
|
-
idle: idle,
|
216
|
-
active: active,
|
217
|
-
available: available
|
218
|
-
)
|
180
|
+
begin
|
181
|
+
if conn.last_checked_out
|
182
|
+
previous_checkin, conn.last_checked_in = conn.last_checked_in, Time.now
|
183
|
+
idle_manager.log_debug "checking in connection %s at %s (checked out for %.3f seconds)",
|
184
|
+
conn.object_id, conn.last_checked_in,
|
185
|
+
(conn.last_checked_in - conn.last_checked_out).to_f.round(6)
|
186
|
+
else
|
187
|
+
idle_manager.log_debug "checking in connection #{conn.object_id}"
|
188
|
+
end
|
189
|
+
ensure
|
190
|
+
checkin_without_last_checked_in(conn)
|
219
191
|
end
|
220
192
|
end
|
221
193
|
|
222
|
-
def
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
# if we don't have enough available slots (i.e., current pool size
|
230
|
-
# is greater than max pool size) then do nothing
|
231
|
-
return unless open_slots > 0
|
232
|
-
|
233
|
-
# otherwise, spin up connections up to our min_idle_connections setting
|
234
|
-
create_count = min_idle_connections - idle_count
|
235
|
-
create_count = open_slots if create_count > open_slots
|
236
|
-
|
237
|
-
warmup_connections(create_count)
|
194
|
+
def pool_statistics
|
195
|
+
ActiveSupport::OrderedOptions.new.merge(
|
196
|
+
total: (total = @connections.size),
|
197
|
+
idle: (idle = @connections.count(&:idle?)),
|
198
|
+
active: (active = @connections.count(&:in_use?)),
|
199
|
+
available: (total - active)
|
200
|
+
)
|
238
201
|
end
|
239
202
|
|
240
|
-
def
|
241
|
-
|
242
|
-
slots = connection_limit - @connections.size
|
243
|
-
count = slots if slots < count
|
244
|
-
|
245
|
-
return unless slots >= count
|
246
|
-
|
247
|
-
idle_manager.log_info "Warming up #{count} connection#{count > 1 ? 's' : ''}"
|
248
|
-
synchronize do
|
249
|
-
count.times {
|
250
|
-
conn = checkout_new_connection
|
251
|
-
@available.add conn
|
252
|
-
}
|
253
|
-
end
|
203
|
+
def idle_connections
|
204
|
+
@connections.select(&:idle?).sort
|
254
205
|
end
|
255
206
|
|
256
207
|
def remove_idle_connections
|
257
208
|
# don't attempt to remove idle connections if we have threads waiting
|
258
209
|
if @available.num_waiting > 0
|
259
|
-
idle_manager.log_warn "
|
210
|
+
idle_manager.log_warn "Skipping reap while #{@available.num_waiting} thread(s) are actively waiting on database connections..."
|
260
211
|
return
|
261
212
|
end
|
262
213
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
unless idle_count > max_idle_connections
|
267
|
-
idle_manager.log_warn "idle count (#{idle_count}) does not exceed max idle connections (#{max_idle_connections}); skipping reap."
|
268
|
-
return
|
269
|
-
end
|
214
|
+
return unless (candidates = idle_connections.size - max_idle_connections) > 0
|
215
|
+
idle_manager.log_info "attempting to reap #{candidates} candidate connections"
|
270
216
|
|
271
|
-
|
217
|
+
reaped = 0
|
272
218
|
|
273
|
-
|
274
|
-
|
275
|
-
if idx < cull_count
|
219
|
+
synchronize do
|
220
|
+
idle_connections[0...candidates].each_with_index { |conn, idx|
|
276
221
|
if remove_connection(conn)
|
277
|
-
|
278
|
-
idle_manager.log_info "
|
222
|
+
reaped += 1
|
223
|
+
idle_manager.log_info "reaped candidate connection #%d id#%d age:%d idle:%d" % [
|
224
|
+
idx, conn.object_id, conn.instance_age.to_i, conn.idle_time.to_i
|
225
|
+
]
|
279
226
|
else
|
280
|
-
idle_manager.log_info "kept connection
|
227
|
+
idle_manager.log_info "kept candidate connection #%d id#%d age:%d idle:%d" % [
|
228
|
+
idx, conn.object_id, conn.instance_age.to_i, conn.idle_time.to_i
|
229
|
+
]
|
281
230
|
end
|
282
|
-
|
283
|
-
idle_manager.log_info "kept connection ##{idx} id##{conn.object_id} - age:#{conn.instance_age.to_i} idle_time:#{conn.idle_time.to_i}"
|
284
|
-
end
|
231
|
+
}
|
285
232
|
end
|
286
233
|
|
287
|
-
idle_manager.log_info "
|
234
|
+
idle_manager.log_info "reaped #{reaped} of #{candidates} candidate connections"
|
288
235
|
end
|
289
236
|
|
290
237
|
private
|
291
238
|
|
292
239
|
def remove_connection(conn)
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
conn.disconnect!
|
297
|
-
end
|
240
|
+
return false if conn.in_use?
|
241
|
+
|
242
|
+
remove(conn.tap { |c| c.disconnect! })
|
298
243
|
true
|
299
244
|
end
|
300
245
|
end
|
@@ -38,6 +38,7 @@ module AdvancedConnection::ActiveRecordExt
|
|
38
38
|
class AgeSorted < Default
|
39
39
|
def poll(timeout = nil)
|
40
40
|
synchronize do
|
41
|
+
# always sort age based queues from youngest to oldest
|
41
42
|
@queue.sort_by!(&:instance_age)
|
42
43
|
no_wait_poll || (timeout && wait_poll(timeout))
|
43
44
|
end
|
@@ -46,12 +47,16 @@ module AdvancedConnection::ActiveRecordExt
|
|
46
47
|
|
47
48
|
class YoungAgeBiased < AgeSorted
|
48
49
|
def remove
|
50
|
+
# Think of this like a stack, sorted youngest to oldest, bottom to top. To to
|
51
|
+
# aquire the youngest entry, we shift it off the bottom (i.e., the first element)
|
49
52
|
@queue.shift
|
50
53
|
end
|
51
54
|
end
|
52
55
|
|
53
56
|
class OldAgeBiased < AgeSorted
|
54
57
|
def remove
|
58
|
+
# Think of this like a stack, sorted youngest to oldest, bottom to top. To to
|
59
|
+
# aquire the oldest entry, we pop it off the top (i.e., the last element)
|
55
60
|
@queue.pop
|
56
61
|
end
|
57
62
|
end
|
@@ -24,6 +24,8 @@ module AdvancedConnection::ActiveRecordExt
|
|
24
24
|
module WithoutConnection
|
25
25
|
extend ActiveSupport::Concern
|
26
26
|
|
27
|
+
MAX_REAQUIRE_ATTEMPTS = 3
|
28
|
+
|
27
29
|
included do
|
28
30
|
include ::ActiveSupport::Callbacks
|
29
31
|
alias_method :retrieve_connection, :connection
|
@@ -34,19 +36,25 @@ module AdvancedConnection::ActiveRecordExt
|
|
34
36
|
set_callback :without_connection, :after, :after_without_connection
|
35
37
|
end
|
36
38
|
|
37
|
-
def without_connection
|
39
|
+
def without_connection(&block)
|
38
40
|
return unless block_given?
|
39
41
|
|
42
|
+
# if we're not enabled just execute the block and return
|
43
|
+
return block.call unless AdvancedConnection.enable_without_connection
|
44
|
+
|
45
|
+
# if we have a transaction open, we can't release the database connection
|
46
|
+
# or Bad Things (tm) happen - so we just execute our block and return
|
47
|
+
if transaction_open?
|
48
|
+
Rails.logger.warn "WithoutConnection skipped due to open transaction."
|
49
|
+
return block.call
|
50
|
+
end
|
51
|
+
|
40
52
|
if AdvancedConnection.callbacks.without_connection.present?
|
41
53
|
run_callbacks(:without_connection) do
|
42
|
-
__without_connection
|
43
|
-
yield
|
44
|
-
end
|
54
|
+
__without_connection { block.call }
|
45
55
|
end
|
46
56
|
else
|
47
|
-
__without_connection
|
48
|
-
yield
|
49
|
-
end
|
57
|
+
__without_connection { block.call }
|
50
58
|
end
|
51
59
|
end
|
52
60
|
|
@@ -58,15 +66,19 @@ module AdvancedConnection::ActiveRecordExt
|
|
58
66
|
raise Error::UnableToReleaseConnection if active_connection?
|
59
67
|
yield
|
60
68
|
ensure
|
61
|
-
|
69
|
+
attempt = 0
|
62
70
|
begin
|
63
71
|
# attempt to retrieve another connection
|
64
72
|
retrieve_connection
|
65
73
|
rescue ActiveRecord::ConnectionTimeoutError
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
74
|
+
if attempt >= MAX_REAQUIRE_ATTEMPTS
|
75
|
+
Rails.logger.info "Giving up on trying to reacquire database connection"
|
76
|
+
raise Error::UnableToReaquireConnection
|
77
|
+
else
|
78
|
+
Rails.logger.warn "Failed to reaquire database connection - reattempt #{attempt += 1}/#{MAX_REAQUIRE_ATTEMPTS} ..."
|
79
|
+
end
|
80
|
+
|
81
|
+
retry
|
70
82
|
end
|
71
83
|
end
|
72
84
|
|
@@ -21,10 +21,12 @@
|
|
21
21
|
#
|
22
22
|
require 'singleton'
|
23
23
|
require 'monitor'
|
24
|
+
require 'logger'
|
24
25
|
|
25
26
|
module AdvancedConnection
|
26
27
|
class Config
|
27
28
|
include Singleton
|
29
|
+
include MonitorMixin
|
28
30
|
|
29
31
|
VALID_QUEUE_TYPES = [
|
30
32
|
:fifo, :lifo, :stack, :prefer_younger, :prefer_older
|
@@ -37,12 +39,13 @@ module AdvancedConnection
|
|
37
39
|
).freeze unless defined? CALLBACK_TYPES
|
38
40
|
|
39
41
|
DEFAULT_CONFIG = ActiveSupport::OrderedOptions.new.merge(
|
42
|
+
idle_manager_log_level: ::Logger::INFO,
|
40
43
|
enable_without_connection: false,
|
41
44
|
enable_statement_pooling: false,
|
42
45
|
enable_idle_connection_manager: false,
|
43
46
|
connection_pool_queue_type: :fifo,
|
44
|
-
warmup_connections:
|
45
|
-
min_idle_connections:
|
47
|
+
warmup_connections: nil,
|
48
|
+
min_idle_connections: nil,
|
46
49
|
max_idle_connections: ::Float::INFINITY,
|
47
50
|
max_idle_time: 0,
|
48
51
|
idle_check_interval: nil,
|
@@ -140,6 +143,21 @@ module AdvancedConnection
|
|
140
143
|
@config.callbacks
|
141
144
|
end
|
142
145
|
|
146
|
+
def idle_manager_log_level
|
147
|
+
@config[:log_level]
|
148
|
+
end
|
149
|
+
|
150
|
+
def idle_manager_log_level=(level)
|
151
|
+
@config[:log_level] = case level
|
152
|
+
when :debug, /debug/i, ::Logger::DEBUG then ::Logger::DEBUG
|
153
|
+
when :info, /info/i, ::Logger::INFO then ::Logger::INFO
|
154
|
+
when :warn, /warn/i, ::Logger::WARN then ::Logger::WARN
|
155
|
+
when :error, /error/i, ::Logger::ERROR then ::Logger::ERROR
|
156
|
+
when :fatal, /fatal/i, ::Logger::FATAL then ::Logger::FATAL
|
157
|
+
else ::Logger::INFO
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
143
161
|
def enable_without_connection
|
144
162
|
@config[:enable_without_connection]
|
145
163
|
end
|
@@ -171,28 +189,21 @@ module AdvancedConnection
|
|
171
189
|
end
|
172
190
|
|
173
191
|
def warmup_connections
|
174
|
-
|
192
|
+
# deprecated and not used
|
193
|
+
0
|
175
194
|
end
|
176
195
|
|
177
196
|
def warmup_connections=(value)
|
178
|
-
|
179
|
-
fail Error::ConfigError, 'Expected warmup_connections to be nil, false ' \
|
180
|
-
"or a valid positive integer, but found `#{value.inspect}`"
|
181
|
-
end
|
182
|
-
|
183
|
-
synchronize { @config[:warmup_connections] = value.to_s =~ /^\d+$/ ? value.to_i : false }
|
197
|
+
# deprecated and not used
|
184
198
|
end
|
185
199
|
|
186
200
|
def min_idle_connections
|
187
|
-
|
201
|
+
# deprecated and not used
|
202
|
+
0
|
188
203
|
end
|
189
204
|
|
190
205
|
def min_idle_connections=(value)
|
191
|
-
|
192
|
-
fail Error::ConfigError, 'Expected min_idle_connections to be ' \
|
193
|
-
"a valid integer value, but found `#{value.inspect}`"
|
194
|
-
end
|
195
|
-
synchronize { @config[:min_idle_connections] = value.to_i }
|
206
|
+
# deprecated and not used
|
196
207
|
end
|
197
208
|
|
198
209
|
def max_idle_connections
|
@@ -41,12 +41,6 @@ module AdvancedConnection
|
|
41
41
|
ActiveRecord::Base.send(:include, AdvancedConnection::ActiveRecordExt)
|
42
42
|
}
|
43
43
|
end
|
44
|
-
|
45
|
-
config.after_initialize do
|
46
|
-
if AdvancedConnection.enable_idle_connection_manager && AdvancedConnection.warmup_connections
|
47
|
-
ActiveRecord::Base.connection_handler.connection_pool_list.each(&:warmup_connections)
|
48
|
-
end
|
49
|
-
end
|
50
44
|
end
|
51
45
|
end
|
52
46
|
end
|
@@ -6,42 +6,33 @@ AdvancedConnection.configure do |config|
|
|
6
6
|
## Idle Manager
|
7
7
|
#
|
8
8
|
# Enabling this will enable idle connection management. This allows you to specify settings
|
9
|
-
# to enable automatic
|
10
|
-
#
|
9
|
+
# to enable the automatic reaping of idle database connections.
|
10
|
+
#
|
11
11
|
# config.enable_idle_connection_manager = <%= AdvancedConnection::Config::DEFAULT_CONFIG.enable_idle_connection_manager.inspect %>
|
12
12
|
|
13
13
|
# Pool queue type determines both how free connections will be checkout out
|
14
|
-
# of the pool, as well as how idle connections will be
|
14
|
+
# of the pool, as well as how idle connections will be reaped. The options are:
|
15
15
|
#
|
16
|
-
# :fifo - All connections will have an equal opportunity to be used and
|
16
|
+
# :fifo - All connections will have an equal opportunity to be used and reaped (default)
|
17
17
|
# :lifo/:stack - More frequently used connections will be reused, leaving less frequently used
|
18
|
-
# connections to be
|
18
|
+
# connections to be reaped
|
19
19
|
# :prefer_older - Longer lived connections will tend to stick around longer, with younger
|
20
|
-
# connections being
|
20
|
+
# connections being reaped
|
21
21
|
# :prefer_younger - Younger lived connections will tend to stick around longer, with older
|
22
|
-
# connections being
|
22
|
+
# connections being reaped
|
23
23
|
#
|
24
24
|
# config.connection_pool_queue_type = <%= AdvancedConnection::Config::DEFAULT_CONFIG.connection_pool_queue_type.inspect %>
|
25
25
|
|
26
|
-
#
|
27
|
-
#
|
28
|
-
# responding again.
|
29
|
-
#
|
30
|
-
# config.warmup_connections = <%= AdvancedConnection::Config::DEFAULT_CONFIG.warmup_connections.inspect %>
|
31
|
-
|
32
|
-
# Minimum number of connection to keep idle. If, during the idle check, you have fewer
|
33
|
-
# than this many connections idle, then a number of new connections will be created
|
34
|
-
# up to this this number.
|
35
|
-
#
|
36
|
-
# config.min_idle_connections = <%= AdvancedConnection::Config::DEFAULT_CONFIG.min_idle_connections.inspect %>
|
26
|
+
# What log level to log idle manager stats and details at (see ::Logger#level)
|
27
|
+
# config.idle_manager_log_level = ::Logger::INFO
|
37
28
|
|
38
|
-
# Maximum number of connections that can remain idle without being
|
29
|
+
# Maximum number of connections that can remain idle without being reaped. If you have
|
39
30
|
# more idle conections than this, only the difference between the total idle and this
|
40
|
-
# maximum will be
|
31
|
+
# maximum will be reaped.
|
41
32
|
#
|
42
33
|
# config.max_idle_connections = ::Float::INFINITY
|
43
34
|
|
44
|
-
# How long (in seconds) a connection can remain idle before being
|
35
|
+
# How long (in seconds) a connection can remain idle before being reaped
|
45
36
|
#
|
46
37
|
# config.max_idle_time = 90
|
47
38
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: advanced_connection
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carl P. Corliss
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|