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

问题描述 投票:2回答:1

我正在使用Sinatra 2.0.5,ActiveRecord 5.2.2,Puma 3.12.0在Ruby 2.4.4中构建服务。 (我不使用rails。)

我的代码看起来像这样。我有一个端点打开数据库连接(到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'
...

到目前为止,我的发现过程一直如此。

  1. 我查看了Postgres中的连接用法,认为我可能会泄漏连接 - 不,我似乎没有。
  2. 为了以防万一,我将连接池增加到16(对应16个Puma线程) - 没有帮助。
  3. 然后我查看了ActiveRecord源代码。在这里,我意识到为什么2)没有帮助。问题不在于我无法获得连接,但我无法获得连接池(是的,是的,它在异常中表示)。从中获取连接池的@owner_to_pool映射变量将process_id存储为键,并将值存储为连接池(实际上,该值也是一个映射,其中键是连接规范,我认为该值是一个实际的池实例)。在我的情况下,我只有一个连接规范到我唯一的数据库。 但Puma是一个多线程的网络服务器。它在同一进程中运行所有请求但在不同的线程中运行。 因此,我认为,会发生以下情况: 第一个请求,从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看起来很相似,但遗憾的是它没有答案,我没有足够的声誉来评论它,并询问作者是否解决了它。

ruby activerecord database-connection sinatra puma
1个回答
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

© www.soinside.com 2019 - 2024. All rights reserved.