我正在使用Rails 5和Ruby 2.4。我怎么能弄清楚,或者你能看出下面的内容,是否有多个线程在同一时间运行?
pool = Concurrent::FixedThreadPool.new(1)
promises = links.map do |link|
Concurrent::Promise.execute(executor: pool) do
result = process_link(link)
if result
if result.kind_of?(Array)
result.each do |my_obj|
my_obj.update_attributes({ :a => a })
records_processed = records_processed + my_obj.matches.count
end
else
records_processed = records_processed + result.matches.count
result.update_attributes({ :a => a })
end
end
end
end
promises.map(&:wait).map(&:value!)
由于我已将我的池设置为“1”,我的假设是没有任何内容同时运行,但我不断收到此错误...
Error during processing: (ActiveRecord::ConnectionTimeoutError) could not obtain a connection from the pool within 5.000 seconds (waited 5.002 seconds); all pooled connections were in use
/Users/nataliab/.rvm/gems/ruby-2.4.0@global/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:202:in `block in wait_poll'
/Users/nataliab/.rvm/gems/ruby-2.4.0@global/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:193:in `loop'
/Users/nataliab/.rvm/gems/ruby-2.4.0@global/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:193:in `wait_poll'
/Users/nataliab/.rvm/gems/ruby-2.4.0@global/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:154:in `internal_poll'
/Users/nataliab/.rvm/gems/ruby-2.4.0@global/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:278:in `internal_poll'
/Users/nataliab/.rvm/gems/ruby-2.4.0@global/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:148:in `block in poll'
/Users/nataliab/.rvm/rubies/ruby-2.4.0/lib/ruby/2.4.0/monitor.rb:214:in `mon_synchronize'
/Users/nataliab/.rvm/gems/ruby-2.4.0@global/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:158:in `synchronize'
/Users/nataliab/.rvm/gems/ruby-2.4.0@global/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:148:in `poll'
/Users/nataliab/.rvm/gems/ruby-2.4.0@global/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:717:in `acquire_connection'
/Users/nataliab/.rvm/gems/ruby-2.4.0@global/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:490:in `checkout'
/Users/nataliab/.rvm/gems/ruby-2.4.0@global/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:364:in `connection'
/Users/nataliab/.rvm/gems/ruby-2.4.0@global/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:883:in `retrieve_connection'
/Users/nataliab/.rvm/gems/ruby-2.4.0@global/gems/activerecord-5.0.1/lib/active_record/connection_handling.rb:128:in `retrieve_connection'
如果我修改我的代码以运行我肯定没有并发的情况,我不会得到上述错误...
links.each do |link|
result = process_link(link)
if result
if result.kind_of?(Array)
result.each do |race|
my_obj.update_attributes({ :a => a })
records_processed = records_processed + my_obj.matches.count
end
else
records_processed = records_processed + result.matches.count
result.update_attributes({ :a => a })
end
end
end
编辑:这是我的开发环境的数据库配置。另请注意,所有这些都在rails控制台中运行。
development:
adapter: postgresql
encoding: utf8
database: sims
username: postgres
password: password
pool: 5
timeout: 15000
host: 127.0.0.1
答案 0 :(得分:7)
您假设多个线程必须仅因为连接池耗尽而并发运行是不正确的。仅仅因为连接仍然从连接池“检出”并不意味着当前正在线程中的签出连接上执行查询,这只是意味着线程的连接尚未被检入。线程可以处于空闲状态,但只要尚未明确终止,它仍然可以保持连接池的连接。
由于ActiveRecord Connections是线程本地的,因此您可以通过在多个线程上运行ActiveRecord查询来耗尽连接池,就像在这种情况下一样。 (每次调用Concurrent::FixedThreadPool.new(1)
时,都会创建一个新线程。)即使您一次只在一个线程上运行查询,默认情况下,每个线程上的连接仍将保持打开状态,直到它们被终止
为了避免这种情况,您可以在使用它们之后手动签入连接,或者确保您的线程被终止(终止),以便池可以恢复(收集)它们的连接。
要手动签入连接,请参阅ConnectionPool
documentation以了解您的选项。最简单的方法是将ActiveRecord代码包装在with_connection
块中:
Concurrent::Promise.execute(executor: pool) do
ActiveRecord::Base.connection_pool.with_connection do
# update_attributes, etc
end
end
要确保所有线程都被终止,请在完成使用后在线程池上调用#shutdown
后跟#wait_for_termination
:
values = promises.map(&:value!)
pool.shutdown
pool.wait_for_termination
答案 1 :(得分:4)
您假设只有一个线程不正确。有两个 - 线程池中的一个,以及在线程池中生成一个的主要一个。
当您使主线程等待并且它不应该访问数据库时,您可能会感到困惑。这并不意味着它仍然没有连接,因此阻止其他线程获取连接。
根据经验,您的数据库连接池应设置为至少产生的线程数 + 1.在这种情况下 - 2.
易于重现的代码:
# migration
class CreateFoos < ActiveRecord::Migration[5.0]
def change
create_table :foos do |t|
t.integer :bar
end
end
end
# model
class Foo < ApplicationRecord
end
# rake task
task experiment: :environment do
Foo.create
pool = Concurrent::FixedThreadPool.new(1)
promise =
Concurrent::Promise.execute(executor: pool) do
Foo.first.update_attributes!(bar: rand(-42..42))
end
promise.wait.value!
end
在pool
中将config/database.yml
设置为1并运行任务。你会收到一个错误。将它设置为2 - 就可以了。
您可以增加池中的线程数,并至少添加那么多承诺来进行处理。对于数据库连接池 = 线程池中的线程数,您将始终失败,如果在config/database.yml
中再添加一个,则会成功。
答案 2 :(得分:3)
由于您将固定线程池定义为具有一个线程,因此我假设您没有实现任何类型的并发。看看你的错误,看来池中的一个可用线程忙了太久,导致了连接超时异常。
当您更改代码实现以使其不包含线程池时,应用程序显式为单线程,而不会因为等待池中的线程而导致连接超时。尝试增加线程池的大小(可能增加到3或5)并查看是否仍然得到相同的异常。