Fix: https://github.com/rails/rails/issues/45994
A semi-common issue since Ruby 3.0.2 is that using a fiber inside
a transaction cause a deadlock:
```ruby
Post.transaction do
enum = Enumerator.new do |y|
y.yield Post.first # stuck
end
enum.next
end
```
This is because in https://bugs.ruby-lang.org/issues/17827
Ruby changed Monitor to be owned by the calling Fiber rather than Thread.
And since the Active Record connection pool is per Thread, we end
up in a situation where a Fiber tries to acquire a lock owned by another
fiber with no change to ever resolve.
In https://github.com/rails/rails/pull/46519 We've made that lock optional
as it's only needed for system tests.
Now this PR introduce an alternative lock implementation that behave like Monitor
used to up to Ruby 2.7, and we use this one if `ActiveSupport::IsolatedExecutionState.context`
is a Thread.
If it's a Fiber, we continue to use the implementation derived from the stdlib that is Fiber based.
Co-Authored-By: Maple Ong <maple.develops@gmail.com>
This fixes an issue where competing threads deadlock each other.
- Thread A holds the load interlock but is blocked on getting the DB lock
- Thread B holds the DB lock but is blocked on getting the load interlock (for example when there is a `Model.transaction` block that needs to autoload)
This solution allows for dependency loading in other threads while a thread is waiting to acquire the DB lock.
Fixes#31019