aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test
diff options
context:
space:
mode:
authorBrent Wheeldon <brent.wheeldon@gmail.com>2018-01-12 15:58:04 -0500
committerBrent Wheeldon <brent.wheeldon@gmail.com>2018-03-23 15:53:19 -0400
commit0a1ed447999d0092e8d0e86729666fc3b4577151 (patch)
treecf67e13a66869acc5cb10bf8357da272340c3f74 /activerecord/test
parent6aa5cf03ea8232180ffbbae4c130b051f813c670 (diff)
downloadrails-0a1ed447999d0092e8d0e86729666fc3b4577151.tar.gz
rails-0a1ed447999d0092e8d0e86729666fc3b4577151.tar.bz2
rails-0a1ed447999d0092e8d0e86729666fc3b4577151.zip
Prevent deadlocks when waiting for connection from pool.
When a thread that had the load interlock but was blocked waiting to check a connection out of the connection pool but all of the threads using the available connections were blocked waiting to obtain the load interlock an `ActiveRecord::ConnectionTimeoutError` exception was be thrown by the thread waiting for the connection. When waiting for the connection to check out we should allow loading to proceed to avoid this deadlock.
Diffstat (limited to 'activerecord/test')
-rw-r--r--activerecord/test/cases/connection_pool_test.rb38
1 files changed, 38 insertions, 0 deletions
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index 9ac03629c3..b02d7fb4fd 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -109,6 +109,44 @@ module ActiveRecord
assert_equal connection, t.join.value
end
+ def test_full_pool_blocking_shares_load_interlock
+ @pool.instance_variable_set(:@size, 1)
+
+ load_interlock_latch = Concurrent::CountDownLatch.new
+ connection_latch = Concurrent::CountDownLatch.new
+
+ able_to_get_connection = false
+ able_to_load = false
+
+ thread_with_load_interlock = Thread.new do
+ ActiveSupport::Dependencies.interlock.running do
+ load_interlock_latch.count_down
+ connection_latch.wait
+
+ @pool.with_connection do
+ able_to_get_connection = true
+ end
+ end
+ end
+
+ thread_with_last_connection = Thread.new do
+ @pool.with_connection do
+ connection_latch.count_down
+ load_interlock_latch.wait
+
+ ActiveSupport::Dependencies.interlock.loading do
+ able_to_load = true
+ end
+ end
+ end
+
+ thread_with_load_interlock.join
+ thread_with_last_connection.join
+
+ assert able_to_get_connection
+ assert able_to_load
+ end
+
def test_removing_releases_latch
cs = @pool.size.times.map { @pool.checkout }
t = Thread.new { @pool.checkout }