11

I am trying to implement a multithreaded method in rails such that I can create/update multiple records very quickly.

This is the outline of my program.

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

This gives me warnings on my log.

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.

And it gives me an error

could not obtain a database connection within 5 seconds (waited 5.059358 seconds). 
The max pool size is currently 5; consider increasing it.

I tried: - changing database.yaml and increasing the poolsize and timeout there. - modified the existing code in the following way.

   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

I am still getting the same warning and error. I am obviously missing the concept here. How do I proceed with this?

2 Answers 2

31

To prevent connection leak in multi threads, you have to manage the connection manually. You can try:

Thread.new do
  ActiveRecord::Base.connection_pool.with_connection do
    # Do whatever
  end
end

One problem of ActiveRecord::Base.connection_pool.with_connection is that it always prepare a connection even if the code inside does not need it.

We can improve it a little bit by using ActiveRecord::Base.connection_pool.release_connection:

Thread.new do
  begin
    # Do whatever
  ensure
    ActiveRecord::Base.connection_pool.release_connection
  end
end
Sign up to request clarification or add additional context in comments.

2 Comments

This method only works for connections that have been obtained through connection or with_connection methods, connections obtained through checkout will not be automatically released. source: api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/…
@anemaria20 Yes, it says this. But even if you don't use with_connection, explicitly, that method works. E.g. you can try - (1..1000).each{ Thread.new { puts Model.count; ActiveRecord::Base.connection_pool.release_connection; sleep rand(10) } }; nil It will actually wait until any connection is released if the pool is full, but w/o explicit release_connection it will give you pool timeout error
0

if you want to run in N threads, make sure you have N connections in the pool

#  ActiveRecord::Base.connection_config
#  # => {pool: 5, timeout: 5000, database: "db/development.sqlite3", adapter: "sqlite3"}

otherwise you will be able to fetch a connection from the pool.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.