We meet error after app upgrade to rails 7.2 and ruby 3.2.2.
ActiveRecord::StatementInvalid Mysql2::Error: This connection is in use by: #<Fiber:0x00007fad145210e8 (resumed)>
It is occurred in heavy loading mode. Why it could be happened? It looks like threadsafe error, but we do not use threads, except Puma (version 5.6.5)
I faced too with this error. This is because rails 7.2 new behaviour:
ApplicationRecord.connection_pool.with_connection do
puts ApplicationRecord.connection_pool.stat
ApplicationRecord.lease_connection # OR ApplicationRecord.connection
puts ApplicationRecord.connection_pool.stat
end
puts ApplicationRecord.connection_pool.stat
# =>
{:size=>16, :connections=>1, :busy=>1, :dead=>0, :idle=>0, :waiting=>0, :checkout_timeout=>5.0}
{:size=>16, :connections=>1, :busy=>1, :dead=>0, :idle=>0, :waiting=>0, :checkout_timeout=>5.0}
{:size=>16, :connections=>1, :busy=>1, :dead=>0, :idle=>0, :waiting=>0, :checkout_timeout=>5.0}
#with_connection will no longer release connection if you call #lease_connection or #connection inside block anywhere
And #release_connection will release connection inside #with_connection block:
ApplicationRecord.connection_pool.with_connection do
puts ApplicationRecord.connection_pool.stat
ApplicationRecord.release_connection
puts ApplicationRecord.connection_pool.stat
end
# =>
{:size=>16, :connections=>1, :busy=>1, :dead=>0, :idle=>0, :waiting=>0, :checkout_timeout=>5.0}
{:size=>16, :connections=>1, :busy=>0, :dead=>0, :idle=>1, :waiting=>0, :checkout_timeout=>5.0}
Also, if you work with connection from block argument(.with_connection do |conn|) you will be faced with "this connection is used by FIber" Error:
ApplicationRecord.connection_pool.with_connection do |conn|
# # Lets imagine that we have legacy logic that call #release_connection:
ApplicationRecord.connection_pool.with_connection do
ApplicationRecord.release_connection
end
# And let's simulate heavy load to db
ApplicationRecord.connection_pool.size.times do
Thread.new { ApplicationRecord.lease_connection.execute('SELECT SLEEP(10);') }
end
conn.execute('SELECT SLEEP(1);')
puts ApplicationRecord.connection_pool.stat
end
Solutions:
Dont use #with_connection, use anywhere #lease_connection and explicitly call #release_connection
Dont use #lease_connection and #release_connection, use only #with_connection and pass it argument anywhere
Write your own func which will repeat old behaviour of #with_connection:
def with_connection
connection_active_was = ApplicationRecord.connection_pool.active_connection?
ApplicationRecord.connection_pool.with_connection(&block)
ensure
ApplicationRecord.release_connection unless connection_active_was
end
I selected solution with func