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:
parent
6aa5cf03ea
commit
0a1ed44799
2 changed files with 41 additions and 1 deletions
|
@ -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?
|
||||
|
||||
|
|
|
@ -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 }
|
||||
|
|
Loading…
Reference in a new issue