activerecord-bogacs 0.6.0 → 0.7.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/.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
|