aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Draper <matthew@trebex.net>2016-07-07 07:40:26 +0930
committerGitHub <noreply@github.com>2016-07-07 07:40:26 +0930
commitaeba05d389a846908cedbafa3a82eb781ae0f19d (patch)
tree51538a6aabc93247cef3da4bb0d70a47014ae5ec
parentd5ca0a8b8e4a9ab301ba37a335e62ada872e7a76 (diff)
parent61f4b1ff8a54ccd7e70bb4f6c0b958b28bc648fc (diff)
downloadrails-aeba05d389a846908cedbafa3a82eb781ae0f19d.tar.gz
rails-aeba05d389a846908cedbafa3a82eb781ae0f19d.tar.bz2
rails-aeba05d389a846908cedbafa3a82eb781ae0f19d.zip
Merge pull request #25707 from matthewd/double-reap
Don't reap connections that have already been reassigned
-rw-r--r--activerecord/CHANGELOG.md7
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb24
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb25
-rw-r--r--activerecord/test/cases/connection_pool_test.rb5
4 files changed, 50 insertions, 11 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 8521d3b07b..4637847987 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,10 @@
+* Ensure concurrent invocations of the connection reaper cannot allocate the
+ same connection to two threads.
+
+ Fixes #25585.
+
+ *Matthew Draper*
+
* Inspecting an object with an associated array of over 10 elements no longer
truncates the array, preventing `inspect` from looping infinitely in some
cases.
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index a006e4f50f..9b74c3a10f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -415,7 +415,10 @@ module ActiveRecord
with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
synchronize do
@connections.each do |conn|
- checkin conn
+ if conn.in_use?
+ conn.steal!
+ checkin conn
+ end
conn.disconnect!
end
@connections = []
@@ -447,7 +450,10 @@ module ActiveRecord
with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
synchronize do
@connections.each do |conn|
- checkin conn
+ if conn.in_use?
+ conn.steal!
+ checkin conn
+ end
conn.disconnect! if conn.requires_reloading?
end
@connections.delete_if(&:requires_reloading?)
@@ -556,17 +562,17 @@ module ActiveRecord
stale_connections = synchronize do
@connections.select do |conn|
conn.in_use? && !conn.owner.alive?
+ end.each do |conn|
+ conn.steal!
end
end
stale_connections.each do |conn|
- synchronize do
- if conn.active?
- conn.reset!
- checkin conn
- else
- remove conn
- end
+ if conn.active?
+ conn.reset!
+ checkin conn
+ else
+ remove conn
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index d4b9e301bc..5747e4d1ee 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -184,7 +184,30 @@ module ActiveRecord
# this method must only be called while holding connection pool's mutex
def expire
- @owner = nil
+ if in_use?
+ if @owner != Thread.current
+ raise ActiveRecordError, "Cannot expire connection, " <<
+ "it is owned by a different thread: #{@owner}. " <<
+ "Current thread: #{Thread.current}."
+ end
+
+ @owner = nil
+ else
+ raise ActiveRecordError, 'Cannot expire connection, it is not currently leased.'
+ end
+ end
+
+ # this method must only be called while holding connection pool's mutex (and a desire for segfaults)
+ def steal! # :nodoc:
+ if in_use?
+ if @owner != Thread.current
+ pool.send :remove_connection_from_thread_cache, self, @owner
+
+ @owner = Thread.current
+ end
+ else
+ raise ActiveRecordError, 'Cannot steal connection, it is not currently leased.'
+ end
end
def unprepared_statement
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index a45ee281c7..09e7848bda 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -151,7 +151,7 @@ module ActiveRecord
assert_equal 1, active_connections(@pool).size
ensure
- @pool.connections.each(&:close)
+ @pool.connections.each { |conn| conn.close if conn.in_use? }
end
def test_remove_connection
@@ -432,6 +432,9 @@ module ActiveRecord
Thread.new { @pool.send(group_action_method) }.join
# assert connection has been forcefully taken away from us
assert_not @pool.active_connection?
+
+ # make a new connection for with_connection to clean up
+ @pool.connection
end
end
end