2017-02-03 12 views
15

Я использую 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/[email protected]/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:202:in `block in wait_poll' 
/Users/nataliab/.rvm/gems/[email protected]/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:193:in `loop' 
/Users/nataliab/.rvm/gems/ruby[email protected]/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:193:in `wait_poll' 
/Users/nataliab/.rvm/gems/[email protected]/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:154:in `internal_poll' 
/Users/nataliab/.rvm/gems/[email protected]/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:278:in `internal_poll' 
/Users/nataliab/.rvm/gems/[email protected]/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/[email protected]/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:158:in `synchronize' 
/Users/nataliab/.rvm/gems/[email protected]/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:148:in `poll' 
/Users/nataliab/.rvm/gems/[email protected]/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:717:in `acquire_connection' 
/Users/nataliab/.rvm/gems/[email protected]/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:490:in `checkout' 
/Users/nataliab/.rvm/gems/[email protected]/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:364:in `connection' 
/Users/nataliab/.rvm/gems/[email protected]/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:883:in `retrieve_connection' 
/Users/nataliab/.rvm/gems/[email protected]/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 

Edit: Это моя конфигурация базы данных для моего Envir развития onment. Также обратите внимание, что все это выполняется в консоли rails.

development: 
    adapter: postgresql 
    encoding: utf8 
    database: sims 
    username: postgres 
    password: password 
    pool: 5 
    timeout: 15000 
    host: 127.0.0.1 
+0

Итак, вы все еще получаете это на полностью чистой замене? – ndn

ответ

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 
    
3

Поскольку вы определяете фиксированный пул потоков, чтобы иметь один поток, я бы предположил, что вы не достигаете какого-либо параллелизма. Глядя на вашу ошибку, кажется, что один доступный поток из пула слишком долго занят и вызвал исключение таймаута соединения.

Когда вы изменили реализацию кода, чтобы он не содержал пул потоков, приложение было явно однопоточным без возможности таймаута подключения из-за ожидания потоков из пула. Попробуйте увеличить размер пула потоков (возможно, до 3 или 5) и посмотреть, все ли вы получаете то же исключение.

+0

Когда вы говорите «что один доступный поток из пула слишком долго занят и вызвал исключение таймаута соединения», как мне это проверить - например, как установить период ожидания для потока? –

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 на номер 1 вашего config/database.yml и запустите задачу. Вы получите сообщение об ошибке. Установите его на 2 - это будет просто отлично.

Вы можете увеличить количество потоков в пуле и добавить, по крайней мере, столько обещаний на обработку. Вы будете постоянно отказываться от пула подключения к базе данных = количество потоков в пуле потоков и преуспеть, если вы добавите еще один в config/database.yml.

+0

Я включил мою конфигурацию базы данных в свой вопрос. Он был установлен на 5 намного выше того, что вы предложили. Тем не менее, я все еще получаю ошибку. Я считаю, что существует более одного потока выполнения, но если бы это было всего 2, почему бы мне получить erorr, когда мой пул соединений установлен в 5? –

+0

@ Наталия, тогда это действительно странно. Вы используете многопоточный сервер, такой как Puma? – ndn

+0

Я запускаю это через консоль рельсов. Это не использует веб-серверы, верно? –

 Смежные вопросы

  • Нет связанных вопросов^_^