activerecord-bogacs 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +82 -0
- data/Gemfile +2 -15
- data/Rakefile +8 -6
- data/activerecord-bogacs.gemspec +6 -6
- data/lib/active_record/bogacs/connection_handler.rb +1 -1
- data/lib/active_record/bogacs/default_pool.rb +250 -90
- data/lib/active_record/bogacs/false_pool.rb +87 -53
- data/lib/active_record/bogacs/pool_support.rb +26 -8
- data/lib/active_record/bogacs/shareable_pool.rb +11 -15
- data/lib/active_record/bogacs/thread_safe/synchronized.rb +18 -22
- data/lib/active_record/bogacs/thread_safe.rb +3 -90
- data/lib/active_record/bogacs/validator.rb +14 -19
- data/lib/active_record/bogacs/version.rb +1 -1
- data/lib/active_record/connection_adapters/adapter_compat.rb +41 -25
- data/lib/active_record/connection_adapters/pool_class.rb +33 -2
- data/test/active_record/bogacs/false_pool_test.rb +66 -78
- data/test/active_record/bogacs/shareable_pool/connection_pool_test.rb +6 -3
- data/test/active_record/bogacs/shareable_pool/connection_sharing_test.rb +3 -2
- data/test/active_record/bogacs/shareable_pool_helper.rb +1 -1
- data/test/active_record/bogacs/validator_test.rb +22 -28
- data/test/active_record/connection_pool_test_methods.rb +24 -20
- data/test/test_helper.rb +40 -25
- metadata +30 -17
@@ -1,7 +1,8 @@
|
|
1
|
+
require 'active_record/version'
|
2
|
+
|
1
3
|
require 'thread'
|
2
4
|
require 'monitor'
|
3
|
-
|
4
|
-
require 'active_record/version'
|
5
|
+
require 'concurrent/atomic/atomic_boolean'
|
5
6
|
|
6
7
|
require 'active_record/connection_adapters/adapter_compat'
|
7
8
|
require 'active_record/bogacs/pool_support'
|
@@ -18,13 +19,9 @@ module ActiveRecord
|
|
18
19
|
# http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/ConnectionPool.html
|
19
20
|
#
|
20
21
|
class DefaultPool
|
21
|
-
|
22
|
-
# with which it shares a Monitor. But could be a generic Queue.
|
23
|
-
#
|
24
|
-
# The Queue in stdlib's 'thread' could replace this class except
|
25
|
-
# stdlib's doesn't support waiting with a timeout.
|
22
|
+
|
26
23
|
# @private
|
27
|
-
class Queue
|
24
|
+
class Queue # ConnectionLeasingQueue
|
28
25
|
def initialize(lock)
|
29
26
|
@lock = lock
|
30
27
|
@cond = @lock.new_cond
|
@@ -82,18 +79,25 @@ module ActiveRecord
|
|
82
79
|
#
|
83
80
|
# @raise [ActiveRecord::ConnectionTimeoutError] if +timeout+ given and no element
|
84
81
|
# becomes available after +timeout+ seconds
|
85
|
-
def poll(timeout = nil
|
86
|
-
synchronize
|
87
|
-
if timeout
|
88
|
-
no_wait_poll || wait_poll(timeout, &block)
|
89
|
-
else
|
90
|
-
no_wait_poll
|
91
|
-
end
|
92
|
-
end
|
82
|
+
def poll(timeout = nil)
|
83
|
+
synchronize { internal_poll(timeout) }
|
93
84
|
end
|
94
85
|
|
95
86
|
private
|
96
87
|
|
88
|
+
def internal_poll(timeout)
|
89
|
+
conn = no_wait_poll || (timeout && wait_poll(timeout))
|
90
|
+
# Connections must be leased while holding the main pool mutex. This is
|
91
|
+
# an internal subclass that also +.leases+ returned connections while
|
92
|
+
# still in queue's critical section (queue synchronizes with the same
|
93
|
+
# <tt>@lock</tt> as the main pool) so that a returned connection is already
|
94
|
+
# leased and there is no need to re-enter synchronized block.
|
95
|
+
#
|
96
|
+
# NOTE: avoid the need for ConnectionLeasingQueue, since BiasableQueue is not implemented
|
97
|
+
conn.lease if conn
|
98
|
+
conn
|
99
|
+
end
|
100
|
+
|
97
101
|
def synchronize(&block)
|
98
102
|
@lock.synchronize(&block)
|
99
103
|
end
|
@@ -104,21 +108,21 @@ module ActiveRecord
|
|
104
108
|
end
|
105
109
|
|
106
110
|
# A thread can remove an element from the queue without
|
107
|
-
# waiting if
|
111
|
+
# waiting if and only if the number of currently available
|
108
112
|
# connections is strictly greater than the number of waiting
|
109
113
|
# threads.
|
110
114
|
def can_remove_no_wait?
|
111
115
|
@queue.size > @num_waiting
|
112
116
|
end
|
113
117
|
|
114
|
-
# Removes and returns the head of the queue if possible, or nil
|
118
|
+
# Removes and returns the head of the queue if possible, or +nil+.
|
115
119
|
def remove
|
116
|
-
@queue.
|
120
|
+
@queue.pop
|
117
121
|
end
|
118
122
|
|
119
123
|
# Remove and return the head the queue if the number of
|
120
124
|
# available elements is strictly greater than the number of
|
121
|
-
# threads currently waiting.
|
125
|
+
# threads currently waiting. Otherwise, return +nil+.
|
122
126
|
def no_wait_poll
|
123
127
|
remove if can_remove_no_wait?
|
124
128
|
end
|
@@ -126,13 +130,10 @@ module ActiveRecord
|
|
126
130
|
# Waits on the queue up to +timeout+ seconds, then removes and
|
127
131
|
# returns the head of the queue.
|
128
132
|
def wait_poll(timeout)
|
129
|
-
t0 = Time.now
|
130
|
-
elapsed = 0
|
131
|
-
|
132
133
|
@num_waiting += 1
|
133
134
|
|
134
|
-
|
135
|
-
|
135
|
+
t0 = Time.now
|
136
|
+
elapsed = 0
|
136
137
|
while true
|
137
138
|
@cond.wait(timeout - elapsed)
|
138
139
|
|
@@ -156,8 +157,8 @@ module ActiveRecord
|
|
156
157
|
require 'active_record/bogacs/reaper.rb'
|
157
158
|
|
158
159
|
attr_accessor :automatic_reconnect, :checkout_timeout
|
159
|
-
attr_reader :spec, :
|
160
|
-
attr_reader :initial_size
|
160
|
+
attr_reader :spec, :size, :reaper
|
161
|
+
attr_reader :validator, :initial_size
|
161
162
|
|
162
163
|
# Creates a new `ConnectionPool` object. +spec+ is a ConnectionSpecification
|
163
164
|
# object which describes database connection information (e.g. adapter,
|
@@ -186,8 +187,12 @@ module ActiveRecord
|
|
186
187
|
|
187
188
|
@spec = spec
|
188
189
|
|
189
|
-
@checkout_timeout = ( spec.config[:checkout_timeout] ||
|
190
|
-
|
190
|
+
@checkout_timeout = ( spec.config[:checkout_timeout] || 5 ).to_f
|
191
|
+
if @idle_timeout = spec.config.fetch(:idle_timeout, 300)
|
192
|
+
@idle_timeout = @idle_timeout.to_f
|
193
|
+
@idle_timeout = nil if @idle_timeout <= 0
|
194
|
+
end
|
195
|
+
|
191
196
|
@reaper = Reaper.new self, spec.config[:reaping_frequency]
|
192
197
|
@reaping = !! @reaper.run
|
193
198
|
|
@@ -202,13 +207,24 @@ module ActiveRecord
|
|
202
207
|
end
|
203
208
|
|
204
209
|
# The cache of reserved connections mapped to threads
|
205
|
-
@
|
210
|
+
@thread_cached_conns = ThreadSafe::Map.new(initial_capacity: @size)
|
206
211
|
|
207
212
|
@connections = []
|
208
213
|
@automatic_reconnect = true
|
209
214
|
|
215
|
+
# Connection pool allows for concurrent (outside the main +synchronize+ section)
|
216
|
+
# establishment of new connections. This variable tracks the number of threads
|
217
|
+
# currently in the process of independently establishing connections to the DB.
|
218
|
+
@now_connecting = 0
|
219
|
+
|
220
|
+
@threads_blocking_new_connections = 0 # TODO: dummy for now
|
221
|
+
|
210
222
|
@available = Queue.new self
|
211
223
|
|
224
|
+
@lock_thread = false
|
225
|
+
|
226
|
+
@connected = ::Concurrent::AtomicBoolean.new
|
227
|
+
|
212
228
|
initial_size = spec.config[:pool_initial] || 0
|
213
229
|
initial_size = @size if initial_size == true
|
214
230
|
initial_size = (@size * initial_size).to_i if initial_size <= 1.0
|
@@ -232,12 +248,9 @@ module ActiveRecord
|
|
232
248
|
#
|
233
249
|
# @return [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
234
250
|
def connection
|
235
|
-
connection_id = current_connection_id
|
236
|
-
|
237
|
-
|
238
|
-
conn = ( @reserved_connections[connection_id] ||= checkout )
|
239
|
-
end
|
240
|
-
end
|
251
|
+
connection_id = current_connection_id(current_thread)
|
252
|
+
conn = @thread_cached_conns.fetch(connection_id, nil)
|
253
|
+
conn = ( @thread_cached_conns[connection_id] ||= checkout ) unless conn
|
241
254
|
conn
|
242
255
|
end
|
243
256
|
|
@@ -245,22 +258,16 @@ module ActiveRecord
|
|
245
258
|
#
|
246
259
|
# @return [true, false]
|
247
260
|
def active_connection?
|
248
|
-
connection_id = current_connection_id
|
249
|
-
|
250
|
-
!! conn.in_use? # synchronize { conn.in_use? }
|
251
|
-
else
|
252
|
-
false
|
253
|
-
end
|
261
|
+
connection_id = current_connection_id(current_thread)
|
262
|
+
@thread_cached_conns.fetch(connection_id, nil)
|
254
263
|
end
|
255
264
|
|
256
265
|
# Signal that the thread is finished with the current connection.
|
257
266
|
# #release_connection releases the connection-thread association
|
258
267
|
# and returns the connection to the pool.
|
259
|
-
def release_connection(
|
260
|
-
|
261
|
-
|
262
|
-
checkin conn, true if conn
|
263
|
-
#end
|
268
|
+
def release_connection(owner_thread = Thread.current)
|
269
|
+
conn = @thread_cached_conns.delete(current_connection_id(owner_thread))
|
270
|
+
checkin conn if conn
|
264
271
|
end
|
265
272
|
|
266
273
|
# If a connection already exists yield it to the block. If no connection
|
@@ -270,25 +277,48 @@ module ActiveRecord
|
|
270
277
|
# @yield [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
271
278
|
def with_connection
|
272
279
|
connection_id = current_connection_id
|
273
|
-
|
274
|
-
|
280
|
+
unless conn = @thread_cached_conns[connection_id]
|
281
|
+
conn = connection
|
282
|
+
fresh_connection = true
|
283
|
+
end
|
284
|
+
yield conn
|
275
285
|
ensure
|
276
|
-
release_connection
|
286
|
+
release_connection if fresh_connection
|
277
287
|
end
|
278
288
|
|
279
289
|
# Returns true if a connection has already been opened.
|
280
290
|
#
|
281
291
|
# @return [true, false]
|
282
292
|
def connected?
|
283
|
-
@
|
293
|
+
@connected.true? # synchronize { @connections.any? }
|
294
|
+
end
|
295
|
+
|
296
|
+
# Returns an array containing the connections currently in the pool.
|
297
|
+
# Access to the array does not require synchronization on the pool because
|
298
|
+
# the array is newly created and not retained by the pool.
|
299
|
+
#
|
300
|
+
# However; this method bypasses the ConnectionPool's thread-safe connection
|
301
|
+
# access pattern. A returned connection may be owned by another thread,
|
302
|
+
# unowned, or by happen-stance owned by the calling thread.
|
303
|
+
#
|
304
|
+
# Calling methods on a connection without ownership is subject to the
|
305
|
+
# thread-safety guarantees of the underlying method. Many of the methods
|
306
|
+
# on connection adapter classes are inherently multi-thread unsafe.
|
307
|
+
def connections
|
308
|
+
synchronize { @connections.dup }
|
284
309
|
end
|
285
310
|
|
286
311
|
# Disconnects all connections in the pool, and clears the pool.
|
287
312
|
def disconnect!
|
288
313
|
synchronize do
|
289
|
-
@
|
314
|
+
@connected.make_false
|
315
|
+
|
316
|
+
@thread_cached_conns.clear
|
290
317
|
@connections.each do |conn|
|
291
|
-
|
318
|
+
if conn.in_use?
|
319
|
+
conn.steal!
|
320
|
+
checkin conn
|
321
|
+
end
|
292
322
|
conn.disconnect!
|
293
323
|
end
|
294
324
|
@connections.clear
|
@@ -296,21 +326,39 @@ module ActiveRecord
|
|
296
326
|
end
|
297
327
|
end
|
298
328
|
|
299
|
-
#
|
329
|
+
# Discards all connections in the pool (even if they're currently
|
330
|
+
# leased!), along with the pool itself. Any further interaction with the
|
331
|
+
# pool (except #spec and #schema_cache) is undefined.
|
332
|
+
#
|
333
|
+
# See AbstractAdapter#discard!
|
334
|
+
def discard! # :nodoc:
|
335
|
+
synchronize do
|
336
|
+
return if @connections.nil? # already discarded
|
337
|
+
@connected.make_false
|
338
|
+
|
339
|
+
@connections.each do |conn|
|
340
|
+
conn.discard!
|
341
|
+
end
|
342
|
+
@connections = @available = @thread_cached_conns = nil
|
343
|
+
end
|
344
|
+
end
|
300
345
|
def clear_reloadable_connections!
|
301
346
|
synchronize do
|
302
|
-
@
|
347
|
+
@thread_cached_conns.clear
|
303
348
|
@connections.each do |conn|
|
304
|
-
|
349
|
+
if conn.in_use?
|
350
|
+
conn.steal!
|
351
|
+
checkin conn
|
352
|
+
end
|
305
353
|
conn.disconnect! if conn.requires_reloading?
|
306
354
|
end
|
307
|
-
@connections.delete_if
|
308
|
-
conn.requires_reloading?
|
309
|
-
end
|
355
|
+
@connections.delete_if(&:requires_reloading?)
|
310
356
|
@available.clear
|
311
357
|
@connections.each do |conn|
|
312
358
|
@available.add conn
|
313
359
|
end
|
360
|
+
|
361
|
+
@connected.value = @connections.any?
|
314
362
|
end
|
315
363
|
end
|
316
364
|
|
@@ -331,11 +379,11 @@ module ActiveRecord
|
|
331
379
|
# @private AR 3.2 compatibility
|
332
380
|
def clear_stale_cached_connections!
|
333
381
|
keys = Thread.list.find_all { |t| t.alive? }.map(&:object_id)
|
334
|
-
keys = @
|
382
|
+
keys = @thread_cached_conns.keys - keys
|
335
383
|
keys.each do |key|
|
336
|
-
conn = @
|
384
|
+
conn = @thread_cached_conns[key]
|
337
385
|
checkin conn
|
338
|
-
@
|
386
|
+
@thread_cached_conns.delete(key)
|
339
387
|
end
|
340
388
|
end if ActiveRecord::VERSION::MAJOR < 4
|
341
389
|
|
@@ -350,28 +398,25 @@ module ActiveRecord
|
|
350
398
|
# @raise [ActiveRecord::ConnectionTimeoutError] if all connections are leased
|
351
399
|
# and the pool is at capacity (meaning the number of currently leased
|
352
400
|
# connections is greater than or equal to the size limit set)
|
353
|
-
def checkout
|
354
|
-
|
355
|
-
synchronize do
|
356
|
-
conn = acquire_connection
|
357
|
-
conn.lease
|
358
|
-
end
|
359
|
-
checkout_and_verify(conn)
|
401
|
+
def checkout(checkout_timeout = @checkout_timeout)
|
402
|
+
checkout_and_verify(acquire_connection(checkout_timeout))
|
360
403
|
end
|
361
404
|
|
362
|
-
# Check-in a database connection back into the pool
|
405
|
+
# Check-in a database connection back into the pool, indicating that you
|
406
|
+
# no longer need this connection.
|
363
407
|
#
|
364
|
-
#
|
365
|
-
#
|
366
|
-
|
367
|
-
|
408
|
+
# +conn+: an AbstractAdapter object, which was obtained by earlier by
|
409
|
+
# calling #checkout on this pool.
|
410
|
+
def checkin(conn)
|
411
|
+
#conn.lock.synchronize do
|
368
412
|
synchronize do
|
369
|
-
|
413
|
+
remove_connection_from_thread_cache conn
|
370
414
|
|
371
|
-
|
415
|
+
_run_checkin_callbacks(conn)
|
372
416
|
|
373
417
|
@available.add conn
|
374
418
|
end
|
419
|
+
#end
|
375
420
|
end
|
376
421
|
|
377
422
|
# Remove a connection from the connection pool. The returned connection
|
@@ -379,14 +424,25 @@ module ActiveRecord
|
|
379
424
|
#
|
380
425
|
# @return [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
381
426
|
def remove(conn)
|
427
|
+
needs_new_connection = false
|
428
|
+
|
382
429
|
synchronize do
|
430
|
+
remove_connection_from_thread_cache conn
|
431
|
+
|
383
432
|
@connections.delete conn
|
384
433
|
@available.delete conn
|
385
434
|
|
386
|
-
|
435
|
+
@connected.value = @connections.any?
|
387
436
|
|
388
|
-
|
437
|
+
needs_new_connection = @available.any_waiting?
|
389
438
|
end
|
439
|
+
|
440
|
+
# This is intentionally done outside of the synchronized section as we
|
441
|
+
# would like not to hold the main mutex while checking out new connections.
|
442
|
+
# Thus there is some chance that needs_new_connection information is now
|
443
|
+
# stale, we can live with that (bulk_make_new_connections will make
|
444
|
+
# sure not to exceed the pool's @size limit).
|
445
|
+
bulk_make_new_connections(1) if needs_new_connection
|
390
446
|
end
|
391
447
|
|
392
448
|
# Recover lost connections for the pool. A lost connection can occur if
|
@@ -396,6 +452,8 @@ module ActiveRecord
|
|
396
452
|
stale_connections = synchronize do
|
397
453
|
@connections.select do |conn|
|
398
454
|
conn.in_use? && !conn.owner.alive?
|
455
|
+
end.each do |conn|
|
456
|
+
conn.steal!
|
399
457
|
end
|
400
458
|
end
|
401
459
|
|
@@ -410,6 +468,61 @@ module ActiveRecord
|
|
410
468
|
end
|
411
469
|
end
|
412
470
|
end
|
471
|
+
|
472
|
+
# Disconnect all connections that have been idle for at least
|
473
|
+
# +minimum_idle+ seconds. Connections currently checked out, or that were
|
474
|
+
# checked in less than +minimum_idle+ seconds ago, are unaffected.
|
475
|
+
def flush(minimum_idle = @idle_timeout)
|
476
|
+
return if minimum_idle.nil?
|
477
|
+
|
478
|
+
idle_connections = synchronize do
|
479
|
+
@connections.select do |conn|
|
480
|
+
!conn.in_use? && conn.seconds_idle >= minimum_idle
|
481
|
+
end.each do |conn|
|
482
|
+
conn.lease
|
483
|
+
|
484
|
+
@available.delete conn
|
485
|
+
@connections.delete conn
|
486
|
+
|
487
|
+
@connected.value = @connections.any?
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
idle_connections.each do |conn|
|
492
|
+
conn.disconnect!
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
# Disconnect all currently idle connections. Connections currently checked
|
497
|
+
# out are unaffected.
|
498
|
+
def flush!
|
499
|
+
reap
|
500
|
+
flush(-1)
|
501
|
+
end
|
502
|
+
|
503
|
+
def num_waiting_in_queue # :nodoc:
|
504
|
+
@available.num_waiting
|
505
|
+
end
|
506
|
+
private :num_waiting_in_queue
|
507
|
+
|
508
|
+
# Return connection pool's usage statistic
|
509
|
+
# Example:
|
510
|
+
#
|
511
|
+
# ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
|
512
|
+
def stat
|
513
|
+
synchronize do
|
514
|
+
{
|
515
|
+
size: size,
|
516
|
+
connections: @connections.size,
|
517
|
+
busy: @connections.count { |c| c.in_use? && c.owner.alive? },
|
518
|
+
dead: @connections.count { |c| c.in_use? && !c.owner.alive? },
|
519
|
+
idle: @connections.count { |c| !c.in_use? },
|
520
|
+
waiting: num_waiting_in_queue,
|
521
|
+
checkout_timeout: checkout_timeout
|
522
|
+
}
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
413
526
|
# NOTE: active? and reset! are >= AR 2.3
|
414
527
|
|
415
528
|
def reaper?; (@reaper ||= nil) && @reaper.frequency end
|
@@ -424,6 +537,16 @@ module ActiveRecord
|
|
424
537
|
|
425
538
|
private
|
426
539
|
|
540
|
+
def bulk_make_new_connections(num_new_conns_needed)
|
541
|
+
num_new_conns_needed.times do
|
542
|
+
# try_to_checkout_new_connection will not exceed pool's @size limit
|
543
|
+
if new_conn = try_to_checkout_new_connection
|
544
|
+
# make the new_conn available to the starving threads stuck @available Queue
|
545
|
+
checkin(new_conn)
|
546
|
+
end
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
427
550
|
# Acquire a connection by one of 1) immediately removing one
|
428
551
|
# from the queue of available connections, 2) creating a new
|
429
552
|
# connection if the pool is not at capacity, 3) waiting on the
|
@@ -431,36 +554,73 @@ module ActiveRecord
|
|
431
554
|
#
|
432
555
|
# @raise [ActiveRecord::ConnectionTimeoutError]
|
433
556
|
# @raise [ActiveRecord::ConnectionNotEstablished]
|
434
|
-
def acquire_connection
|
435
|
-
if conn = @available.poll
|
557
|
+
def acquire_connection(checkout_timeout)
|
558
|
+
if conn = @available.poll || try_to_checkout_new_connection
|
436
559
|
conn
|
437
|
-
elsif @connections.size < @size
|
438
|
-
checkout_new_connection
|
439
560
|
else
|
440
561
|
reap unless @reaping
|
441
|
-
@available.poll(
|
562
|
+
@available.poll(checkout_timeout)
|
442
563
|
end
|
443
564
|
end
|
444
565
|
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
566
|
+
#--
|
567
|
+
# if owner_thread param is omitted, this must be called in synchronize block
|
568
|
+
def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
|
569
|
+
@thread_cached_conns.delete_pair(current_connection_id(owner_thread), conn)
|
570
|
+
end
|
571
|
+
alias_method :release, :remove_connection_from_thread_cache
|
572
|
+
|
573
|
+
# If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
|
574
|
+
# to the DB is done outside main synchronized section.
|
575
|
+
#--
|
576
|
+
# Implementation constraint: a newly established connection returned by this
|
577
|
+
# method must be in the +.leased+ state.
|
578
|
+
def try_to_checkout_new_connection
|
579
|
+
# first in synchronized section check if establishing new conns is allowed
|
580
|
+
# and increment @now_connecting, to prevent overstepping this pool's @size
|
581
|
+
# constraint
|
582
|
+
do_checkout = synchronize do
|
583
|
+
if @threads_blocking_new_connections.zero? && (@connections.size + @now_connecting) < @size
|
584
|
+
@now_connecting += 1
|
585
|
+
end
|
586
|
+
end
|
587
|
+
if do_checkout
|
588
|
+
begin
|
589
|
+
# if successfully incremented @now_connecting establish new connection
|
590
|
+
# outside of synchronized section
|
591
|
+
conn = checkout_new_connection
|
592
|
+
ensure
|
593
|
+
synchronize do
|
594
|
+
if conn
|
595
|
+
adopt_connection(conn)
|
596
|
+
# returned conn needs to be already leased
|
597
|
+
conn.lease
|
598
|
+
end
|
599
|
+
@now_connecting -= 1
|
600
|
+
end
|
601
|
+
end
|
449
602
|
end
|
450
603
|
end
|
451
604
|
|
605
|
+
def adopt_connection(conn)
|
606
|
+
conn.pool = self
|
607
|
+
@connections << conn
|
608
|
+
end
|
609
|
+
|
452
610
|
def checkout_new_connection
|
453
611
|
raise ConnectionNotEstablished unless @automatic_reconnect
|
454
|
-
|
455
612
|
conn = new_connection
|
456
|
-
|
457
|
-
@connections << conn
|
613
|
+
@connected.make_true
|
458
614
|
conn
|
459
615
|
end
|
460
616
|
|
461
617
|
def checkout_and_verify(conn)
|
462
618
|
_run_checkout_callbacks(conn)
|
463
619
|
conn
|
620
|
+
rescue => e
|
621
|
+
remove conn
|
622
|
+
conn.disconnect!
|
623
|
+
raise e
|
464
624
|
end
|
465
625
|
|
466
626
|
def prefill_initial_connections
|