我正在Ruby 2.4.4,Sinatra 2.0.5,ActiveRecord 5.2.2,Puma 3.12.0中构建服务。 (我不使用滑轨。)
我的代码如下所示。我有一个端点可以打开一个数据库连接(到Postgres DB)并运行一些数据库查询,如下所示:
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'
...
到目前为止,我的发现过程是这样的。
然后,我查看了ActiveRecord源。在这里,我意识到了为什么2)没有帮助。问题不是我无法获得连接,而是我无法获得连接池(是的,是的,它在例外中说明了这一点)。从中获得连接池的@owner_to_pool
映射变量将process_id
作为键和值(连接池)存储(实际上,值也是一个映射,其中键是连接规范)我认为该值是一个实际的池实例)。就我而言,我只有一个数据库规范。
但是Puma是多线程Web服务器。它在同一进程中但在不同线程中运行所有请求。
因此,我认为会发生以下情况:
process_id=X
,thread=Y
开始的第一个请求基于establish_connection
“检出” process_id=X
中的连接池,并“接受”该连接池。现在它不在@owner_to_pool
中。process_id=X
开始但不同的thread=Z
的第二个请求尝试执行相同的操作-但process_id=X
中不存在owner_to_pool
的连接池。因此,第二个请求没有获得连接池,并因该异常而失败。process_id=X
将clear_all_connections
的连接池放回原位。虽然我不确定我是否100%正确地理解了所有内容,但是在我看来,这种情况会发生。
现在,我的问题是:这一切我该怎么办? 如何使多线程Puma Web服务器与ActiveRecord的连接池正常工作?
非常感谢!
This question似乎很相似,但不幸的是它没有答案,而且我没有足够的声誉来评论它,并询问作者是否解决了问题。
答案 0 :(得分:1)
因此,基本上,我没有意识到我是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