File tree Expand file tree Collapse file tree 4 files changed +51
-11
lines changed
lib/active_record/connection_adapters Expand file tree Collapse file tree 4 files changed +51
-11
lines changed Original file line number Diff line number Diff line change
1
+ * Ensure concurrent invocations of the connection reaper cannot allocate the
2
+ same connection to two threads.
3
+
4
+ Fixes #25585 .
5
+
6
+ * Matthew Draper*
7
+
8
+
1
9
## Rails 5.0.0 (June 30, 2016) ##
2
10
3
11
* Inspecting an object with an associated array of over 10 elements no longer
Original file line number Diff line number Diff line change @@ -415,7 +415,10 @@ def disconnect(raise_on_acquisition_timeout = true)
415
415
with_exclusively_acquired_all_connections ( raise_on_acquisition_timeout ) do
416
416
synchronize do
417
417
@connections . each do |conn |
418
- checkin conn
418
+ if conn . in_use?
419
+ conn . steal!
420
+ checkin conn
421
+ end
419
422
conn . disconnect!
420
423
end
421
424
@connections = [ ]
@@ -447,7 +450,10 @@ def clear_reloadable_connections(raise_on_acquisition_timeout = true)
447
450
with_exclusively_acquired_all_connections ( raise_on_acquisition_timeout ) do
448
451
synchronize do
449
452
@connections . each do |conn |
450
- checkin conn
453
+ if conn . in_use?
454
+ conn . steal!
455
+ checkin conn
456
+ end
451
457
conn . disconnect! if conn . requires_reloading?
452
458
end
453
459
@connections . delete_if ( &:requires_reloading? )
@@ -556,17 +562,17 @@ def reap
556
562
stale_connections = synchronize do
557
563
@connections . select do |conn |
558
564
conn . in_use? && !conn . owner . alive?
565
+ end . each do |conn |
566
+ conn . steal!
559
567
end
560
568
end
561
569
562
570
stale_connections . each do |conn |
563
- synchronize do
564
- if conn . active?
565
- conn . reset!
566
- checkin conn
567
- else
568
- remove conn
569
- end
571
+ if conn . active?
572
+ conn . reset!
573
+ checkin conn
574
+ else
575
+ remove conn
570
576
end
571
577
end
572
578
end
Original file line number Diff line number Diff line change @@ -184,7 +184,30 @@ def schema_cache=(cache)
184
184
185
185
# this method must only be called while holding connection pool's mutex
186
186
def expire
187
- @owner = nil
187
+ if in_use?
188
+ if @owner != Thread . current
189
+ raise ActiveRecordError , "Cannot expire connection, " <<
190
+ "it is owned by a different thread: #{ @owner } . " <<
191
+ "Current thread: #{ Thread . current } ."
192
+ end
193
+
194
+ @owner = nil
195
+ else
196
+ raise ActiveRecordError , 'Cannot expire connection, it is not currently leased.'
197
+ end
198
+ end
199
+
200
+ # this method must only be called while holding connection pool's mutex (and a desire for segfaults)
201
+ def steal! # :nodoc:
202
+ if in_use?
203
+ if @owner != Thread . current
204
+ pool . send :remove_connection_from_thread_cache , self , @owner
205
+
206
+ @owner = Thread . current
207
+ end
208
+ else
209
+ raise ActiveRecordError , 'Cannot steal connection, it is not currently leased.'
210
+ end
188
211
end
189
212
190
213
def unprepared_statement
Original file line number Diff line number Diff line change @@ -151,7 +151,7 @@ def test_reap_inactive
151
151
152
152
assert_equal 1 , active_connections ( @pool ) . size
153
153
ensure
154
- @pool . connections . each ( & : close)
154
+ @pool . connections . each { | conn | conn . close if conn . in_use? }
155
155
end
156
156
157
157
def test_remove_connection
@@ -432,6 +432,9 @@ def test_bang_versions_of_disconnect_and_clear_reloadable_connections_if_unable_
432
432
Thread . new { @pool . send ( group_action_method ) } . join
433
433
# assert connection has been forcefully taken away from us
434
434
assert_not @pool . active_connection?
435
+
436
+ # make a new connection for with_connection to clean up
437
+ @pool . connection
435
438
end
436
439
end
437
440
end
You can’t perform that action at this time.
0 commit comments