1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

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.
This commit is contained in:
Brent Wheeldon 2018-01-12 15:58:04 -05:00
parent 6aa5cf03ea
commit 0a1ed44799
2 changed files with 41 additions and 1 deletions

View file

@ -188,7 +188,9 @@ module ActiveRecord
t0 = Time.now
elapsed = 0
loop do
@cond.wait(timeout - elapsed)
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@cond.wait(timeout - elapsed)
end
return remove if any?

View file

@ -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 }