应用程序升级到 Rails 7.2 和 ruby 3.2.2 后遇到错误。
ActiveRecord::StatementInvalid Mysql2::Error: This connection is in use by: #<Fiber:0x00007fad145210e8 (resumed)>
重载模式下发生。 为什么会发生这种事?它看起来像线程安全错误,但我们不使用线程,除了 Puma(版本 5.6.5)
我也遇到过这个错误。这是因为 Rails 7.2 新行为:
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}
如果您在块内任何地方调用#lease_connection或#connection,#with_connection将不再释放连接
#release_connection 将释放 #with_connection 块内的连接:
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}
此外,如果您使用来自块参数的连接(.with_connection do |conn|),您将面临“此连接由 FIber 使用”错误:
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
解决方案:
不要使用#with_connection,在任何地方使用#lease_connection并显式调用#release_connection
不要使用#lease_connection和#release_connection,仅使用#with_connection并将其参数传递到任何地方
编写自己的函数,它将重复 #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
我选择了带有 func 的解决方案