Sinatra,Puma,ActiveRecord:未找到带有“主要”的连接池

时间:2019-01-12 10:47:03

标签: ruby activerecord database-connection sinatra puma

我正在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'
...

到目前为止,我的发现过程是这样的。

  1. 我查看了Postgres中的连接使用情况,以为我可能会泄漏连接-不,我似乎没有。
  2. 以防万一,我将连接池增加到16个(对应于16个Puma线程)-并没有帮助。
  3. 然后,我查看了ActiveRecord源。在这里,我意识到了为什么2)没有帮助。问题不是我无法获得连接,而是我无法获得连接池(是的,是的,它在例外中说明了这一点)。从中获得连接池的@owner_to_pool映射变量将process_id作为键和值(连接池)存储(实际上,值也是一个映射,其中键是连接规范)我认为该值是一个实际的池实例)。就我而言,我只有一个数据库规范。

    但是Puma是多线程Web服务器。它在同一进程中但在不同线程中运行所有请求。

    因此,我认为会发生以下情况:

    • process_id=Xthread=Y开始的第一个请求基于establish_connection“检出” process_id=X中的连接池,并“接受”该连接池。现在它不在@owner_to_pool中。
    • 从相同的process_id=X开始但不同的thread=Z的第二个请求尝试执行相同的操作-但process_id=X中不存在owner_to_pool的连接池。因此,第二个请求没有获得连接池,并因该异常而失败。
    • 第一个请求成功完成,并通过调用process_id=Xclear_all_connections的连接池放回原位。
    • 从这一切开始并且在并行线程中没有任何并行请求的另一个请求将成功,因为它将拾取连接池并将其放回没有问题。

虽然我不确定我是否100%正确地理解了所有内容,但是在我看来,这种情况会发生。

现在,我的问题是:这一切我该怎么办? 如何使多线程Puma Web服务器与ActiveRecord的连接池正常工作

非常感谢!


This question似乎很相似,但不幸的是它没有答案,而且我没有足够的声誉来评论它,并询问作者是否解决了问题。

1 个答案:

答案 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