【发布时间】:2019-06-07 02:27:11
【问题描述】:
我正在使用 Ruby 2.4.4 构建服务,使用 Sinatra 2.0.5、ActiveRecord 5.2.2、Puma 3.12.0。 (我没有使用导轨。)
我的代码如下所示。我有一个端点,它打开一个数据库连接(到 Postgres 数据库)并运行一些数据库查询,如下所示:
POST '/endpoint' do
# open a connection
ActiveRecord::Base.establish_connection(@@db_configuration)
# run some queries
db_value = TableModel.find_by(xx: yy)
return whatever
end
after do
# after the endpoint finishes, close all open connections
ActiveRecord::Base.clear_all_connections!
end
当我收到对该端点的两个并行请求时,其中一个请求失败并出现以下错误:
2019-01-12 00:22:07 - ActiveRecord::ConnectionNotEstablished - No connection pool with 'primary' found.:
C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/activerecord-5.2.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:1009:in `retrieve_connection'
C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/activerecord-5.2.2/lib/active_record/connection_handling.rb:118:in `retrieve_connection'
C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/activerecord-5.2.2/lib/active_record/connection_handling.rb:90:in `connection'
C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/activerecord-5.2.2/lib/active_record/core.rb:207:in `find_by'
...
到目前为止,我的发现过程是这样的。
- 我查看了 Postgres 中的连接使用情况,认为我可能会泄漏连接 - 不,我似乎没有。
- 以防万一,我将连接池增加到 16 个(对应于 16 个 Puma 线程)- 没有帮助。
-
然后我查看了 ActiveRecord 源。在这里,我意识到为什么 2) 没有帮助。问题不在于我无法获得连接,而是我无法获得连接池(是的,是的,它在异常中说明了这一点)。
@owner_to_poolmap变量,从中获取一个连接池,将process_id作为key,作为values——连接池(其实value也是一个map,其中key是连接规范,value,我想,是一个实际的池实例)。就我而言,我只有一个连接规范到我唯一的数据库。但是 Puma 是一个多线程网络服务器。它在同一个进程中运行所有请求,但在不同的线程中。
因此,我认为会发生以下情况:
- 第一个请求,从
process_id=X、thread=Y开始,“签出”establish_connection中的连接池,基于process_id=X,并“接受”它。现在它不在@owner_to_pool中了。 - 第二个请求,以相同的
process_id=X开始,但不同的thread=Z,尝试执行相同的操作 - 但process_id=X的连接池在owner_to_pool中不存在。因此,第二个请求没有获得连接池,并因该异常而失败。 - 第一个请求成功完成,并通过调用
clear_all_connections将process_id=X的连接池放回原处。 - 在此之后开始的另一个请求,并且在并行线程中没有任何并行请求,将成功,因为它将拾取连接池并再次将其放回没有问题。
- 第一个请求,从
虽然我不确定我是否 100% 正确理解了所有内容,但在我看来,这样的事情会发生。
现在,我的问题是:我该怎么处理这一切? 如何使多线程 Puma 网络服务器与 ActiveRecord 的连接池正常工作?
提前非常感谢!
This question 看起来很相似,可惜没有答案,我也没有足够的声望来评论它并询问作者是否解决了它。
【问题讨论】:
-
您是否考虑过使用
checkout来检查池中的连接(记住checkin)...?不应在每个请求中创建和销毁连接池,但应检出连接然后释放...参见points 2 & 3 here。
标签: ruby activerecord database-connection sinatra puma