Skip to content

Commit 2537d08

Browse files
committed
Merge pull request rails#25707 from matthewd/double-reap
Don't reap connections that have already been reassigned
1 parent dadc386 commit 2537d08

File tree

4 files changed

+51
-11
lines changed

4 files changed

+51
-11
lines changed

activerecord/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
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+
19
## Rails 5.0.0 (June 30, 2016) ##
210

311
* Inspecting an object with an associated array of over 10 elements no longer

activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,10 @@ def disconnect(raise_on_acquisition_timeout = true)
415415
with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
416416
synchronize do
417417
@connections.each do |conn|
418-
checkin conn
418+
if conn.in_use?
419+
conn.steal!
420+
checkin conn
421+
end
419422
conn.disconnect!
420423
end
421424
@connections = []
@@ -447,7 +450,10 @@ def clear_reloadable_connections(raise_on_acquisition_timeout = true)
447450
with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
448451
synchronize do
449452
@connections.each do |conn|
450-
checkin conn
453+
if conn.in_use?
454+
conn.steal!
455+
checkin conn
456+
end
451457
conn.disconnect! if conn.requires_reloading?
452458
end
453459
@connections.delete_if(&:requires_reloading?)
@@ -556,17 +562,17 @@ def reap
556562
stale_connections = synchronize do
557563
@connections.select do |conn|
558564
conn.in_use? && !conn.owner.alive?
565+
end.each do |conn|
566+
conn.steal!
559567
end
560568
end
561569

562570
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
570576
end
571577
end
572578
end

activerecord/lib/active_record/connection_adapters/abstract_adapter.rb

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,30 @@ def schema_cache=(cache)
184184

185185
# this method must only be called while holding connection pool's mutex
186186
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
188211
end
189212

190213
def unprepared_statement

activerecord/test/cases/connection_pool_test.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def test_reap_inactive
151151

152152
assert_equal 1, active_connections(@pool).size
153153
ensure
154-
@pool.connections.each(&:close)
154+
@pool.connections.each { |conn| conn.close if conn.in_use? }
155155
end
156156

157157
def test_remove_connection
@@ -432,6 +432,9 @@ def test_bang_versions_of_disconnect_and_clear_reloadable_connections_if_unable_
432432
Thread.new { @pool.send(group_action_method) }.join
433433
# assert connection has been forcefully taken away from us
434434
assert_not @pool.active_connection?
435+
436+
# make a new connection for with_connection to clean up
437+
@pool.connection
435438
end
436439
end
437440
end

0 commit comments

Comments
 (0)