我们有一个生产系统运行在以下配置中:
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个队列(默认/高/med优先级),每个队列都有4个线程。我们最近增加了一个新的added作业,它在高队列中每30分钟运行一次,几天后,系统就陷入僵局,无法为连接池生成更多的线程。我们已经追踪到'high‘队列和这个新作业,上次它挂起这个'high’队列时有190个线程,几乎所有线程看起来都是‘连接池’。队列进程上的kill -9和我们的主管重新启动它,这一切都是伟大的5-7天,然后再次停机。
这个新任务在远程DB中创建了许多新的列表,我们有本地记录的本地ActiveRecord模型和超类RemoteList模型。我们使用RemoteModel.establish_connection...。事务打开,写,写.,关闭事务,关闭连接。我们与大量的远程DB交谈,因此这个模型对我们来说很好。
新员工不断地向列表发布者发出呼叫,该列表发布者已经使用了3+多年,没有发布锁。我们可以看到,每次通过旧列表发布服务器向远程DB写入时,都会添加一个新的连接池进程。
我试过:
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,问题就解决了。
发布于 2019-05-21 17:43:19
首先,确定线程泄漏的速度--它是在每个任务运行时发生,还是在一段时间内偶尔发生?如果前者-您很幸运,并且可以尝试通过日志记录ObjectSpace.each_object(Thread).count (注意,这是一个沉重的功能,可能不适合在生产中的高负载)在您的代码中的几个点,试图检测哪里的线程泄漏。
怀疑是Open3.capture3 --它启动两个线程来读取进程的stdin/stdout,另一个线程用于读取进程退出状态,如果在C代码中不使用单独的stderr,我建议切换到capture2,看看线程是否泄漏速度慢了1/3倍。我记得popen3和子进程没有使用stderr,或者没有打开/关闭std流,或者没有读取stdin,现在我已经记不起细节了。
发布于 2019-05-23 10:08:43
这不是一个答案,而是一个关于这个问题的更新,今天上午,我们的一个服务器再次发生故障,其中一个31000+线程运行在一个侧翼任务上。
看起来这是Rails ActiveRecord的一个问题,我们可以用一些测试代码成功地将沉睡的线程抛在后面。它是“CoreDBListModel.connection.close”,它使连接池进入睡眠状态,但由于其状态,它从未收获过。
看起来,当我们从Rails 5.1.5迁移到5.2.1时,问题就开始了,如果我们回过头来看这个问题,这个问题在Rails 6中似乎也存在,因为其他人也提出了类似的问题,名称略有不同,但问题相同:
https://stackoverflow.com/questions/56242038
复制相似问题