Sidekiq,线程用完-Rails离开而未收获connection_pool线程

时间:2019-05-21 15:50:33

标签: ruby-on-rails ruby sidekiq

我们有一个在以下配置中运行的生产系统:

Ruby 2.5.1
Rails 5.2.2
Sidekiq 5.2.5
Sidekiq-cron 1.1.0
Redis 4.1.0
  adapter: postgresql
  pool: 10
  reaping_frequency: 10
  timeout: 5000
  username: ...
  password: ...
  host: ...

它有3个队列(默认/高/中优先级),每个队列有4个线程。我们最近添加了一个新的sidekiq cron作业,该作业在高队列中每30分钟运行一次,几天后,系统进入僵局,无法为连接池生成更多线程。我们已经将其追溯到“高”队列和这个新作业,上一次它挂断该“高”队列时有1900个线程,几乎所有线程都看起来是“连接池”。 kill -9处理队列的过程,我们的主管将其重新启动,并且在5到7天之内一切恢复正常,然后又恢复正常。

此新作业在远程数据库中创建了许多新列表,我们具有用于本地记录的本地ActiveRecord模型和超类RemoteList模型。我们使用RemoteModel.establish_connection...。事务打开,写入,写...,关闭事务,关闭连接。我们与大量的远程数据库进行了交谈,因此该模型对我们来说很好用。

新工作人员反复呼唤已使用了3年以上且未发布锁定的列表发布者。每次通过旧列表发布者写入远程数据库时,我们可以看到添加了一个新的连接池过程。

我尝试过:

  1. 手动从池中获取连接,并为ActiveRecord本身和我们的超类返回一个,两者都不返回。
  2. ActiveRecord::Base.connection_pool.with_connection中包装块

以上两种方法均不起作用,并且线程数随着我们拆分出的每个文件而不断增长,直到达到死锁且不再有线程为止。收割似乎根本没有做任何事情。此工作程序中唯一的不同是,它调用open3.capture3到'C'程序中以比我们在ruby中更快的速度执行一些文件拆分,但是我可以看到生成的shell已经关闭并完成了,但是仍然有这些“连接池”线程。

外面的人都有什么好主意。

谢谢 凯特

列表发布

CoreDBListModel.semaphore.synchronize do
    begin

      .... setup removed....
      CoreDBListModel.establish_connection(@config['database'])

      CoreDBListModel.transaction do
        core = CoreDBListModel.where(:description => list.list_id).first
        core.pending = true
        core.name = list.name
        core.tags = category.name
        core.pcount = list.count
        core.active = list.deleted ? 2:0
        core.save

        ... make list insert data.....
        mass_insert = "INSERT INTO #{mapping['table']} (data_id, data, fulldata) VALUES #{inserts.join(", ")}"
        CoreDBListModel.connection.execute(mass_insert)

      # Mark as completed
        core.pending = false
        core.save

      end

    rescue => e
      @code = 500
      @message = "Failed - #{e.message}, #{e.backtrace[0]}"
      Rails.logger.error("CoreDBList() - Publishing failed - #{list.list_id}")
      Rails.logger.error("CoreDBList() - Publishing failed - #{e.message}")
      Rails.logger.error("CoreDBList() - Publishing failed - #{e.backtrace.first(10).join("\n")}")
    ensure
      begin
        # Close our DB connection
        CoreDBListModel.connection.close
      rescue
      end
    end
  end

下面添加了更多详细信息的答案,但基本上问题出在Rails 5.1.6到5.2.1之间。如果回滚到5.1.6,问题就消失了。

https://github.com/rails/rails/issues/36333

2 个答案:

答案 0 :(得分:1)

首先确定线程泄漏的速率-它是在每个任务运行时发生还是随时间散发?如果是前者-您很幸运,可以在代码中的几个点登录ObjectSpace.each_object(Thread).count(请注意,这是一个繁重的功能,可能不适合生产中的高负载)尝试调试,以尝试检测线程在哪里泄漏。 / p>

可疑的是Open3.capture3-如果您未在C代码中使用单独的stderr,则它将启动两个线程以读取进程的stdin / stdout,并启动另一个线程以读取进程退出状态-到capture2,看看线程泄漏速度是否慢了1/3倍。我记得popen3和子进程没有使用stderr或打开/关闭std流的某种组合或没有读取stdin的问题,现在无法回忆详细信息。

答案 1 :(得分:0)

不是问题的答案,而是问题的最新信息,我们的其中一台服务器今天早晨再次停机,其中一项sidekiq任务的线程超过31000。

这似乎是Rails ActiveRecord的问题,我们可以通过一些测试代码成功地将休眠线程抛在后面。正是“ CoreDBListModel.connection.close”使连接池进入睡眠状态,并且由于其状态而从未收获过。

当我们从Rails 5.1.5迁移到5.2.1时,似乎对我们来说是一个问题,如果回滚到该版本,该问题就消失了,这个问题似乎也与Rails 6中的问题类似。也由其他人提出,名称略有不同,但问题相同:

https://github.com/rails/rails/issues/36333