我正在使用 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_pool
映射变量,从中获取连接池,将process_id
存储为键,并作为值-连接池(实际上,值也是一个映射,其中键是连接规范,我认为值是实际的池实例)。就我而言,我只有一个连接规范到我唯一的数据库。但是 Puma 是一个多线程网络服务器。它在同一进程但在不同线程中运行所有请求。
正因为如此,我认为会发生以下情况:
- 第一个请求,从
process_id=X
开始,thread=Y
,“ checkout ”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看起来很相似,但不幸的是它没有答案,而且我没有足够的声誉来评论它并询问作者是否解决了它。
最佳答案
所以,基本上,我没有意识到我正在establish_connection
创建一个连接池。 (是的,是的,我自己在问题中也是这么说的。不过,我并没有完全意识到这一点。)
我最后做的是这样的:
require ....
# create the connection pool with the required configuration - once; it'll belong to the process
ActiveRecord::Base.establish_connection(db_configuration)
at_exit {
# close all connections on app exit
ActiveRecord::Base.clear_all_connections!
}
class SomeClass < Sinatra::Base
POST '/endpoint' do
# run some queries - they'll automatically use a connection from the pool
db_value = TableModel.find_by(xx: yy)
return whatever
end
end
关于ruby - 西纳特拉、美洲狮、ActiveRecord : No connection pool with 'primary' found,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54158828/