Как управлять открытием и закрытием соединений с базой данных при работе с activerecords и несколькими потоками
Я пытаюсь реализовать многопоточный метод в рельсах, чтобы очень быстро создавать/обновлять несколько записей.
Это контур моей программы.
ActiveRecord::Base.transaction do
(1..10).each do |i|
arr[i] = Thread.new {
<some logic on obj>
...
...
obj.save!
}
end
arr.each {|t| t.join}
end
Это дает мне предупреждения в моем журнале.
DEPRECATION WARNING: Database connections will not be closed automatically,
please close your database connection at the end of the thread by calling `close`
on your connection.
И это дает мне ошибку
could not obtain a database connection within 5 seconds (waited 5.059358 seconds).
The max pool size is currently 5; consider increasing it.
Я попробовал:
- изменение database.yaml и увеличение пула и тайм-аута.
- изменил существующий код следующим образом.
ActiveRecord::Base.connection_pool.clear_stale_cached_connections!
begin
ActiveRecord::Base.transaction do
(1..10).each do |i|
arr[i] = Thread.new {
<some logic on obj>
...
...
obj.save!
ActiveRecord::Base.connection.close
}
end
arr.each {|t| t.join}
end
ensure
ActiveRecord::Base.connection.close if ActiveRecord::Base.connection
ActiveRecord::Base.clear_active_connections!
end
Я все еще получаю то же предупреждение и ошибку.
Я, очевидно, пропустил концепцию здесь. Как это сделать?
Ответы
Ответ 1
Чтобы предотвратить утечку соединения в нескольких потоках, вам необходимо управлять соединением вручную. Вы можете попробовать:
Thread.new do
ActiveRecord::Base.connection_pool.with_connection do
# Do whatever
end
end
Одна из проблем ActiveRecord::Base.connection_pool.with_connection
заключается в том, что она всегда готовит соединение, даже если код внутри не нуждается.
Мы можем немного улучшить его, используя ActiveRecord::Base.connection_pool.release_connection
:
Thread.new do
begin
# Do whatever
ensure
ActiveRecord::Base.connection_pool.release_connection
end
end
Ответ 2
если вы хотите запускать N потоков, убедитесь, что у вас есть N соединений в пуле
# ActiveRecord::Base.connection_config
# # => {pool: 5, timeout: 5000, database: "db/development.sqlite3", adapter: "sqlite3"}
в противном случае вы сможете получить соединение из пула.