如何调试Rails连接池的使用?

问题描述 投票:5回答:2

我遇到了Sidekiq工人的问题。

ActiveRecord::ConnectionTimeoutError: could not obtain a database connection within 5.000 seconds (waited 5.000 seconds)

我遵循有关使用ActiveRecord::ConnectionTimeoutError和适当大的连接池的建议。

我想知道我是否正在耗尽连接池。我正在从size记录connections.lengthActiveRecord::Base.connection_pool,但它们保持恒定大小= 100 connections.length = 5.这表明这不是资源泄漏问题。

我的MySQL服务器配置为允许最多400个并发连接。

我的工作最终看起来像这样:

class MyJob < ActiveJob::Base
  queue_as :default    
  rescue_from StandardError do |exception|
    # clear connections on exception. Not sure if this is a good idea or not.
    ActiveRecord::Base.clear_active_connections!    
  end

  def perform()
    logger.info "size"
    logger.info ActiveRecord::Base.connection_pool.instance_eval { @size }
    logger.info  "connections"
    logger.info ActiveRecord::Base.connection_pool.instance_eval { @connections }.length

    # Ensure connections come from connection pool.
    ActiveRecord::Base.connection_pool.with_connection do |conn|
      # do stuff
    end
  end
end

这是诊断造成这种情况的正确方法,无论是资源匮乏还是泄漏?是否有其他技术可以解决为什么会发生这种情况?

ruby-on-rails activerecord sidekiq rails-activejob
2个回答
2
投票

在我看来,这个ActiveRecord::ConnectionTimeoutError只能出现在一个场景中 - 当有太多想要使用数据库连接的线程时,池已经耗尽,甚至等待免费连接也无济于事(从source code学习)。

在你的情况下,这很奇怪。您只使用25个工作线程,但池设置为100个连接,因此有足够的保留。我仍然怀疑你必须在某个地方产生线程。也许你在工作中做一些线程?也许你使用在你的工作中创建线程的gem?

无论如何,如果你能够重现异常,我建议抓住它并获取所有线程发生时的列表,如下所示:

begin
  # job stuff...      
rescue ActiveRecord::ConnectionTimeoutError
  puts "listing #{Thread.list.count} threads:"
  Thread.list.each_with_index do |t,i| 
    puts "---- thread #{i}: #{t.inspect}"
    puts t.backtrace.take(5)  
  end
end

我希望会有100个或更多的线程,你应该看看它们在回溯中的确切位置。


0
投票

试试ActiveRecord::ConnectionAdapters::ConnectionPool#stat

ActiveRecord::Base.connection_pool.stat 
# => { size: 15, connections: 1, busy: 1, dead: 0, idle: 0, waiting: 0, checkout_timeout: 5 }

来自connection_adapters/abstract/connection_pool.rb,在activerecord 5.2.2.1。

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