Я создаю сервис в Ruby 2.4.4, с Sinatra 2.0.5, ActiveRecord 5.2.2, Puma 3.12.0. (Я не использую рельсы.)
Мой код выглядит следующим образом. У меня есть конечная точка, которая открывает соединение с БД (к БД 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'
...
Мой процесс обнаружения пошел до сих пор.
- Я посмотрел на использование соединения в Postgres, думая, что я мог бы утечь соединения - нет, я, кажется, не.
- На всякий случай я увеличил пул соединений до 16 (соответствует 16 потокам Puma) - не помогло.
Затем я посмотрел на источники ActiveRecord. Здесь я понял, почему 2) не помогло. Проблема не в том, что я не могу получить соединение , но я не могу получить пул соединений (да, да, это говорит об этом в исключении). Переменная @owner_to_pool
map, из которой получается пул соединений, хранит process_id
в качестве ключа, а в качестве значений - пулы соединений (фактически, это значение также является картой, где ключ - это спецификация соединения и значение, Я полагаю, это фактический экземпляр пула). В моем случае у меня есть только одна спецификация соединения с моей единственной базой данных.
Но Puma - это многопоточный веб-сервер. Все запросы выполняются в одном и том же процессе, но в разных потоках.
Из-за этого, я думаю, происходит следующее:
- Первый запрос, начиная с
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 корректно работать с пулом соединений ActiveRecord ?
Заранее большое спасибо!
Этот вопрос кажется похожим, но, к сожалению, у него нет ответа, и у меня недостаточно репутации, чтобы прокомментировать и спросить автора, решили ли они его.