advanced_connection 0.5.14 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 177ee27347e2d89c59e62a37e9c4277581a98a3e
4
- data.tar.gz: 5f986bcfddcf5aae95f198c9b83f9ca03f81d94f
3
+ metadata.gz: 9dd080219f299367a2de7b23c0e7ce0d83bc20d3
4
+ data.tar.gz: d3d6b6cca8b0fc5bd717843f00ac5eea6a922589
5
5
  SHA512:
6
- metadata.gz: 65cc5f19566474a2f05244f5025a2f14046a15a07a0069ee0cf1134cdfd76ad73299544cd0676c2eb8ed8b7b56a4758616fa1989095374e7e774b608927ff2cd
7
- data.tar.gz: fc36d53db639a0c22f2723421e924147b898226a917590ca97df10a2d26e243cca13adf798ded82c3b5abece1d683ba2a197187411d22fd670b24e161790beec
6
+ metadata.gz: df32536d7ff51cea3a0620211290ebec729ecb3c3bc21c6845cea8d2baf8ca300dcb296f6fb3c2704eb14bebc6515a3a305d0df10a1baef19bc48e5ebec8cbfc
7
+ data.tar.gz: dc871c1ee46031e645cfd0417a6c6261dcf708947cab05b7c400007cbdcf1f298eba9edf8ba97e6a188bc3e6c8fe3f398720d42e41d7f662fc89e19e3da9b788
data/.gitignore CHANGED
@@ -6,3 +6,4 @@ test/dummy/db/*.sqlite3-journal
6
6
  test/dummy/log/*.log
7
7
  test/dummy/tmp/
8
8
  test/dummy/.sass-cache
9
+ apartment
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- advanced_connection (0.5.14)
4
+ advanced_connection (0.6.0)
5
5
  activerecord (~> 4.1)
6
6
  activesupport (~> 4.1)
7
7
  rails (~> 4.1)
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 warmup of connections on rails startup, min/max idle connections and
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
- include ConnectionPool::IdleManager
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
- if AdvancedConnection.enable_without_connection
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
- @last_checked_in = Time.now - 1.year
40
- @instantiated_at = Time.now
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
- # this ensures that older connections will be culled
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
- # this ensures that younger connections will be culled
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
- # with fifo / lifo queues, we only care about the
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 culled.
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 :thread, :logger
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
- def log_info(format, *args)
47
- @logger.info(format("#{idle_stats} #{format}", *args))
41
+ @logger.level = AdvancedConnection.idle_manager_log_level || ::Logger::INFO
48
42
  end
49
43
 
50
- def log_debug(format, *args)
51
- @logger.debug(format("#{idle_stats} #{format}", *args))
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 log_warn(format, *args)
55
- @logger.debug(format("#{idle_stats} #{format}", *args))
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 dump_stats
59
- if (stats_dump = Rails.root.join('tmp', 'dump-idle-stats.txt')).exist?
60
- log_info "Dumping statistics"
61
- id_size = self.object_id.to_s.size
62
- @logger.info(format("%3s: %#{id_size}s\t%9s\t%9s", 'IDX', 'OID', 'AGE', 'IDLE'))
63
- @pool.idle_connections.each_with_index do |connection, index|
64
- @logger.info(format("%3d: %#{id_size}d\t%9d\t%9d",
65
- index, connection.object_id, connection.instance_age, connection.idle_time))
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
- !!(stats_dump.unlink rescue true)
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
- "[#{Time.now}] IdleManager (Actv:%d,Avail:%d,Idle:%d,Total:%d):",
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 status
80
- if @thread
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
- def restart
88
- stop.start
89
- end
87
+ log_debug "last run was #{Time.now - last_run} seconds ago" if last_run
90
88
 
91
- def stop
92
- @thread.kill if @thread.alive?
93
- @thread = nil
94
- self
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 start
102
+ def run
98
103
  return unless @interval > 0
99
104
 
100
- @thread ||= Thread.new(@pool, @interval) { |pool, interval|
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 do
117
+ loop {
104
118
  sleep interval
105
119
 
106
- begin
107
- start = Time.now
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
- end
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(&:start)
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(:queue_type,
151
- AdvancedConnection.connection_pool_queue_type).to_s.downcase.to_sym
152
- end
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
- (spec.config[:max_idle_connections] || \
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 min_idle_connections
185
- @min_idle_connections ||= begin
186
- min_idle = (spec.config[:min_idle_connections] || AdvancedConnection.min_idle_connections).to_i
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 connection_limit
193
- @size
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
- conn.last_checked_in = Time.now
198
- idle_manager.log_debug "checking in connection #{conn.object_id} at #{conn.last_checked_in}"
199
- checkin_without_last_checked_in(conn)
200
- end
201
-
202
- def idle_connections
203
- synchronize { @connections.select(&:idle?).sort }
204
- end
205
-
206
- def pool_statistics
207
- synchronize do
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 create_idle_connections
223
- idle_count = idle_connections.size
224
- open_slots = connection_limit - @connections.size
225
-
226
- # if we already have enough idle connections, do nothing
227
- return unless idle_count < min_idle_connections
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 warmup_connections(count = nil)
241
- count ||= warmup_connection_count
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 "Cannot reap while threads actively waiting on db connections"
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
- idle_conns = idle_connections
264
- idle_count = idle_conns.size
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
- cull_count = (idle_count - max_idle_connections)
217
+ reaped = 0
272
218
 
273
- culled = 0
274
- idle_conns.each_with_index do |conn, idx|
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
- culled += 1
278
- idle_manager.log_info "culled connection ##{idx} id##{conn.object_id} - age:#{conn.instance_age.to_i} idle_time:#{conn.idle_time.to_i}"
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 ##{idx} id##{conn.object_id} - age:#{conn.instance_age.to_i} idle_time:#{conn.idle_time.to_i}"
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
- else
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 "culled %d connections" % culled
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
- synchronize do
294
- return false if conn.in_use?
295
- remove conn
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 do
43
- yield
44
- end
54
+ __without_connection { block.call }
45
55
  end
46
56
  else
47
- __without_connection do
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
- tries = 3
69
+ attempt = 0
62
70
  begin
63
71
  # attempt to retrieve another connection
64
72
  retrieve_connection
65
73
  rescue ActiveRecord::ConnectionTimeoutError
66
- Rails.logger.info "Failed to acquire a connection (#{Thread.current.object_id}) trying #{tries > 1 ? "#{tries} more times" : 'once more'}"
67
- retry unless (tries -= 1) < 0
68
- Rails.logger.info "Giving up on trying to acquire another connection"
69
- raise
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: false,
45
- min_idle_connections: 0,
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
- @config[:warmup_connections]
192
+ # deprecated and not used
193
+ 0
175
194
  end
176
195
 
177
196
  def warmup_connections=(value)
178
- unless !!value || value.is_a?(Fixnum) || value =~ /^\d+$/
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
- @config[:min_idle_connections]
201
+ # deprecated and not used
202
+ 0
188
203
  end
189
204
 
190
205
  def min_idle_connections=(value)
191
- unless value.is_a?(Numeric) || value =~ /^\d+$/
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
@@ -23,5 +23,6 @@ module AdvancedConnection
23
23
  class Error < StandardError
24
24
  class ConfigError < Error; end
25
25
  class UnableToReleaseConnection < Error; end
26
+ class UnableToReaquireConnection < Error; end
26
27
  end
27
28
  end
@@ -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
@@ -21,8 +21,8 @@
21
21
  #
22
22
  module AdvancedConnection
23
23
  MAJOR = 0
24
- MINOR = 5
25
- PATCH = 14
24
+ MINOR = 6
25
+ PATCH = 0
26
26
 
27
27
  VERSION = "%d.%d.%d" % [ MAJOR, MINOR, PATCH ]
28
28
  GEM_VERSION = Gem::Version.new(VERSION)
@@ -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 warmup of connections on rails startup, min/max idle connections and
10
- # idle connection culling.
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 culled. The options are:
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 culled (default)
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 culled
18
+ # connections to be reaped
19
19
  # :prefer_older - Longer lived connections will tend to stick around longer, with younger
20
- # connections being culled
20
+ # connections being reaped
21
21
  # :prefer_younger - Younger lived connections will tend to stick around longer, with older
22
- # connections being culled
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
- # How many connections to prestart on initial startup of rails. This can
27
- # help to reduce the time it takes a restarted production node to start
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 culled. If you have
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 culled.
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 culled
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.5.14
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-05-13 00:00:00.000000000 Z
11
+ date: 2016-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails