diff options
author | Brent Wheeldon <brent.wheeldon@gmail.com> | 2018-01-12 15:58:04 -0500 |
---|---|---|
committer | Brent Wheeldon <brent.wheeldon@gmail.com> | 2018-03-23 15:53:19 -0400 |
commit | 0a1ed447999d0092e8d0e86729666fc3b4577151 (patch) | |
tree | cf67e13a66869acc5cb10bf8357da272340c3f74 /activerecord/test | |
parent | 6aa5cf03ea8232180ffbbae4c130b051f813c670 (diff) | |
download | rails-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.rb | 38 |
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 } |