activerecord-bogacs 0.5.1 → 0.7.1
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/LICENSE.txt +1 -1
- data/README.md +37 -17
- data/Rakefile +8 -6
- data/activerecord-bogacs.gemspec +6 -6
- data/lib/active_record/bogacs/connection_handler.rb +36 -0
- data/lib/active_record/bogacs/default_pool.rb +256 -91
- data/lib/active_record/bogacs/false_pool.rb +97 -54
- data/lib/active_record/bogacs/pool_support.rb +26 -8
- data/lib/active_record/bogacs/railtie.rb +17 -0
- data/lib/active_record/bogacs/shareable_pool.rb +41 -46
- data/lib/active_record/bogacs/thread_safe/synchronized.rb +18 -22
- data/lib/active_record/bogacs/thread_safe.rb +3 -67
- data/lib/active_record/bogacs/validator.rb +15 -20
- data/lib/active_record/bogacs/version.rb +1 -1
- data/lib/active_record/bogacs.rb +6 -2
- data/lib/active_record/connection_adapters/adapter_compat.rb +63 -17
- data/lib/active_record/connection_adapters/pool_class.rb +33 -2
- data/lib/activerecord-bogacs.rb +1 -0
- data/test/active_record/bogacs/false_pool_test.rb +66 -78
- data/test/active_record/bogacs/shareable_pool/connection_pool_test.rb +2 -1
- data/test/active_record/bogacs/shareable_pool/connection_sharing_test.rb +2 -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 +41 -26
- metadata +33 -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 =
|
236
|
-
|
237
|
-
|
238
|
-
conn = ( @reserved_connections[connection_id] ||= checkout )
|
239
|
-
end
|
240
|
-
end
|
251
|
+
connection_id = connection_cache_key(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 =
|
249
|
-
|
250
|
-
!! conn.in_use? # synchronize { conn.in_use? }
|
251
|
-
else
|
252
|
-
false
|
253
|
-
end
|
261
|
+
connection_id = connection_cache_key(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(connection_cache_key(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
|
@@ -269,26 +276,49 @@ module ActiveRecord
|
|
269
276
|
#
|
270
277
|
# @yield [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
271
278
|
def with_connection
|
272
|
-
connection_id =
|
273
|
-
|
274
|
-
|
279
|
+
connection_id = connection_cache_key
|
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,44 @@ 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 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
|
345
|
+
|
346
|
+
def discarded? # :nodoc:
|
347
|
+
@connections.nil?
|
348
|
+
end
|
349
|
+
|
300
350
|
def clear_reloadable_connections!
|
301
351
|
synchronize do
|
302
|
-
@
|
352
|
+
@thread_cached_conns.clear
|
303
353
|
@connections.each do |conn|
|
304
|
-
|
354
|
+
if conn.in_use?
|
355
|
+
conn.steal!
|
356
|
+
checkin conn
|
357
|
+
end
|
305
358
|
conn.disconnect! if conn.requires_reloading?
|
306
359
|
end
|
307
|
-
@connections.delete_if
|
308
|
-
conn.requires_reloading?
|
309
|
-
end
|
360
|
+
@connections.delete_if(&:requires_reloading?)
|
310
361
|
@available.clear
|
311
362
|
@connections.each do |conn|
|
312
363
|
@available.add conn
|
313
364
|
end
|
365
|
+
|
366
|
+
@connected.value = @connections.any?
|
314
367
|
end
|
315
368
|
end
|
316
369
|
|
@@ -331,11 +384,11 @@ module ActiveRecord
|
|
331
384
|
# @private AR 3.2 compatibility
|
332
385
|
def clear_stale_cached_connections!
|
333
386
|
keys = Thread.list.find_all { |t| t.alive? }.map(&:object_id)
|
334
|
-
keys = @
|
387
|
+
keys = @thread_cached_conns.keys - keys
|
335
388
|
keys.each do |key|
|
336
|
-
conn = @
|
389
|
+
conn = @thread_cached_conns[key]
|
337
390
|
checkin conn
|
338
|
-
@
|
391
|
+
@thread_cached_conns.delete(key)
|
339
392
|
end
|
340
393
|
end if ActiveRecord::VERSION::MAJOR < 4
|
341
394
|
|
@@ -350,28 +403,25 @@ module ActiveRecord
|
|
350
403
|
# @raise [ActiveRecord::ConnectionTimeoutError] if all connections are leased
|
351
404
|
# and the pool is at capacity (meaning the number of currently leased
|
352
405
|
# 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)
|
406
|
+
def checkout(checkout_timeout = @checkout_timeout)
|
407
|
+
checkout_and_verify(acquire_connection(checkout_timeout))
|
360
408
|
end
|
361
409
|
|
362
|
-
# Check-in a database connection back into the pool
|
410
|
+
# Check-in a database connection back into the pool, indicating that you
|
411
|
+
# no longer need this connection.
|
363
412
|
#
|
364
|
-
#
|
365
|
-
#
|
366
|
-
|
367
|
-
|
413
|
+
# +conn+: an AbstractAdapter object, which was obtained by earlier by
|
414
|
+
# calling #checkout on this pool.
|
415
|
+
def checkin(conn)
|
416
|
+
#conn.lock.synchronize do
|
368
417
|
synchronize do
|
369
|
-
|
418
|
+
remove_connection_from_thread_cache conn
|
370
419
|
|
371
|
-
|
420
|
+
_run_checkin_callbacks(conn)
|
372
421
|
|
373
422
|
@available.add conn
|
374
423
|
end
|
424
|
+
#end
|
375
425
|
end
|
376
426
|
|
377
427
|
# Remove a connection from the connection pool. The returned connection
|
@@ -379,14 +429,25 @@ module ActiveRecord
|
|
379
429
|
#
|
380
430
|
# @return [ActiveRecord::ConnectionAdapters::AbstractAdapter]
|
381
431
|
def remove(conn)
|
432
|
+
needs_new_connection = false
|
433
|
+
|
382
434
|
synchronize do
|
435
|
+
remove_connection_from_thread_cache conn
|
436
|
+
|
383
437
|
@connections.delete conn
|
384
438
|
@available.delete conn
|
385
439
|
|
386
|
-
|
440
|
+
@connected.value = @connections.any?
|
387
441
|
|
388
|
-
|
442
|
+
needs_new_connection = @available.any_waiting?
|
389
443
|
end
|
444
|
+
|
445
|
+
# This is intentionally done outside of the synchronized section as we
|
446
|
+
# would like not to hold the main mutex while checking out new connections.
|
447
|
+
# Thus there is some chance that needs_new_connection information is now
|
448
|
+
# stale, we can live with that (bulk_make_new_connections will make
|
449
|
+
# sure not to exceed the pool's @size limit).
|
450
|
+
bulk_make_new_connections(1) if needs_new_connection
|
390
451
|
end
|
391
452
|
|
392
453
|
# Recover lost connections for the pool. A lost connection can occur if
|
@@ -396,6 +457,8 @@ module ActiveRecord
|
|
396
457
|
stale_connections = synchronize do
|
397
458
|
@connections.select do |conn|
|
398
459
|
conn.in_use? && !conn.owner.alive?
|
460
|
+
end.each do |conn|
|
461
|
+
conn.steal!
|
399
462
|
end
|
400
463
|
end
|
401
464
|
|
@@ -410,6 +473,61 @@ module ActiveRecord
|
|
410
473
|
end
|
411
474
|
end
|
412
475
|
end
|
476
|
+
|
477
|
+
# Disconnect all connections that have been idle for at least
|
478
|
+
# +minimum_idle+ seconds. Connections currently checked out, or that were
|
479
|
+
# checked in less than +minimum_idle+ seconds ago, are unaffected.
|
480
|
+
def flush(minimum_idle = @idle_timeout)
|
481
|
+
return if minimum_idle.nil?
|
482
|
+
|
483
|
+
idle_connections = synchronize do
|
484
|
+
@connections.select do |conn|
|
485
|
+
!conn.in_use? && conn.seconds_idle >= minimum_idle
|
486
|
+
end.each do |conn|
|
487
|
+
conn.lease
|
488
|
+
|
489
|
+
@available.delete conn
|
490
|
+
@connections.delete conn
|
491
|
+
|
492
|
+
@connected.value = @connections.any?
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
idle_connections.each do |conn|
|
497
|
+
conn.disconnect!
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
# Disconnect all currently idle connections. Connections currently checked
|
502
|
+
# out are unaffected.
|
503
|
+
def flush!
|
504
|
+
reap
|
505
|
+
flush(-1)
|
506
|
+
end
|
507
|
+
|
508
|
+
def num_waiting_in_queue # :nodoc:
|
509
|
+
@available.num_waiting
|
510
|
+
end
|
511
|
+
private :num_waiting_in_queue
|
512
|
+
|
513
|
+
# Return connection pool's usage statistic
|
514
|
+
# Example:
|
515
|
+
#
|
516
|
+
# ActiveRecord::Base.connection_pool.stat # => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }
|
517
|
+
def stat
|
518
|
+
synchronize do
|
519
|
+
{
|
520
|
+
size: size,
|
521
|
+
connections: @connections.size,
|
522
|
+
busy: @connections.count { |c| c.in_use? && c.owner.alive? },
|
523
|
+
dead: @connections.count { |c| c.in_use? && !c.owner.alive? },
|
524
|
+
idle: @connections.count { |c| !c.in_use? },
|
525
|
+
waiting: num_waiting_in_queue,
|
526
|
+
checkout_timeout: checkout_timeout
|
527
|
+
}
|
528
|
+
end
|
529
|
+
end
|
530
|
+
|
413
531
|
# NOTE: active? and reset! are >= AR 2.3
|
414
532
|
|
415
533
|
def reaper?; (@reaper ||= nil) && @reaper.frequency end
|
@@ -424,6 +542,16 @@ module ActiveRecord
|
|
424
542
|
|
425
543
|
private
|
426
544
|
|
545
|
+
def bulk_make_new_connections(num_new_conns_needed)
|
546
|
+
num_new_conns_needed.times do
|
547
|
+
# try_to_checkout_new_connection will not exceed pool's @size limit
|
548
|
+
if new_conn = try_to_checkout_new_connection
|
549
|
+
# make the new_conn available to the starving threads stuck @available Queue
|
550
|
+
checkin(new_conn)
|
551
|
+
end
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
427
555
|
# Acquire a connection by one of 1) immediately removing one
|
428
556
|
# from the queue of available connections, 2) creating a new
|
429
557
|
# connection if the pool is not at capacity, 3) waiting on the
|
@@ -431,36 +559,73 @@ module ActiveRecord
|
|
431
559
|
#
|
432
560
|
# @raise [ActiveRecord::ConnectionTimeoutError]
|
433
561
|
# @raise [ActiveRecord::ConnectionNotEstablished]
|
434
|
-
def acquire_connection
|
435
|
-
if conn = @available.poll
|
562
|
+
def acquire_connection(checkout_timeout)
|
563
|
+
if conn = @available.poll || try_to_checkout_new_connection
|
436
564
|
conn
|
437
|
-
elsif @connections.size < @size
|
438
|
-
checkout_new_connection
|
439
565
|
else
|
440
566
|
reap unless @reaping
|
441
|
-
@available.poll(
|
567
|
+
@available.poll(checkout_timeout)
|
442
568
|
end
|
443
569
|
end
|
444
570
|
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
571
|
+
#--
|
572
|
+
# if owner_thread param is omitted, this must be called in synchronize block
|
573
|
+
def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
|
574
|
+
@thread_cached_conns.delete_pair(connection_cache_key(owner_thread), conn)
|
575
|
+
end
|
576
|
+
alias_method :release, :remove_connection_from_thread_cache
|
577
|
+
|
578
|
+
# If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
|
579
|
+
# to the DB is done outside main synchronized section.
|
580
|
+
#--
|
581
|
+
# Implementation constraint: a newly established connection returned by this
|
582
|
+
# method must be in the +.leased+ state.
|
583
|
+
def try_to_checkout_new_connection
|
584
|
+
# first in synchronized section check if establishing new conns is allowed
|
585
|
+
# and increment @now_connecting, to prevent overstepping this pool's @size
|
586
|
+
# constraint
|
587
|
+
do_checkout = synchronize do
|
588
|
+
if @threads_blocking_new_connections.zero? && (@connections.size + @now_connecting) < @size
|
589
|
+
@now_connecting += 1
|
590
|
+
end
|
591
|
+
end
|
592
|
+
if do_checkout
|
593
|
+
begin
|
594
|
+
# if successfully incremented @now_connecting establish new connection
|
595
|
+
# outside of synchronized section
|
596
|
+
conn = checkout_new_connection
|
597
|
+
ensure
|
598
|
+
synchronize do
|
599
|
+
if conn
|
600
|
+
adopt_connection(conn)
|
601
|
+
# returned conn needs to be already leased
|
602
|
+
conn.lease
|
603
|
+
end
|
604
|
+
@now_connecting -= 1
|
605
|
+
end
|
606
|
+
end
|
449
607
|
end
|
450
608
|
end
|
451
609
|
|
610
|
+
def adopt_connection(conn)
|
611
|
+
conn.pool = self
|
612
|
+
@connections << conn
|
613
|
+
end
|
614
|
+
|
452
615
|
def checkout_new_connection
|
453
616
|
raise ConnectionNotEstablished unless @automatic_reconnect
|
454
|
-
|
455
617
|
conn = new_connection
|
456
|
-
|
457
|
-
@connections << conn
|
618
|
+
@connected.make_true
|
458
619
|
conn
|
459
620
|
end
|
460
621
|
|
461
622
|
def checkout_and_verify(conn)
|
462
623
|
_run_checkout_callbacks(conn)
|
463
624
|
conn
|
625
|
+
rescue => e
|
626
|
+
remove conn
|
627
|
+
conn.disconnect!
|
628
|
+
raise e
|
464
629
|
end
|
465
630
|
|
466
631
|
def prefill_initial_connections
|