升级到rails 7.2和ruby 3.2.2后出现“此连接已被光纤使用”错误

问题描述 投票:0回答:1

应用程序升级到 Rails 7.2 和 ruby 3.2.2 后遇到错误。

ActiveRecord::StatementInvalid Mysql2::Error: This connection is in use by: #<Fiber:0x00007fad145210e8 (resumed)>

重载模式下发生。 为什么会发生这种事?它看起来像线程安全错误,但我们不使用线程,除了 Puma(版本 5.6.5)

ruby-on-rails rails-activerecord puma mysql2 ruby-on-rails-7.2
1个回答
0
投票

我也遇到过这个错误。这是因为 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 的解决方案

© www.soinside.com 2019 - 2024. All rights reserved.