Does concurrency happen even when only one thread is in a thread pool? Does concurrency happen even when only one thread is in a thread pool? multithreading multithreading

Does concurrency happen even when only one thread is in a thread pool?


Your assumption that multiple threads must be running concurrently just because the connection pool is being exhausted is not correct. Just because a connection is still 'checked out' from the connection pool doesn't mean that a query is currently being executed on the checked-out connection in a thread, it simply means that the thread's connection hasn't been checked back in. The thread could be sitting idle but still holding onto a connection from the connection pool as long as it hasn't been explicitly terminated.

Since ActiveRecord Connections are thread-local, you can exhaust the connection pool by running ActiveRecord queries on multiple threads, as you are doing in this case. (Every time Concurrent::FixedThreadPool.new(1) is called, a new thread is created.) Even if you're only running queries on a single thread at a time, by default a connection will still be held open on every thread until they are terminated.

To avoid this, you can either manually check in connections after using them, or ensure your threads are terminated (killed) so that their connections can be recovered (reaped) by the pool.

  • To manually check in connections, refer to the ConnectionPool documentation for your options. The easiest way is to wrap your ActiveRecord code in a with_connection block:

    Concurrent::Promise.execute(executor: pool) do  ActiveRecord::Base.connection_pool.with_connection do    # update_attributes, etc  endend
  • To ensure all threads are terminated, call #shutdown followed by #wait_for_termination on the thread pool after you're finished using it:

    values = promises.map(&:value!)pool.shutdownpool.wait_for_termination


You assumption that there is only one thread is incorrect. There are two - the one in the thread pool and the main one that spawned the one in the thread pool.

You might be confused as you made the main thread wait and it shouldn't access the database. That doesn't mean that it still doesn't hold a connection, hence preventing the other thread from acquiring one.

As a rule of thumb your database connection pool should be set to at least number of threads spawned + 1. In this case - 2.


Code to easily reproduce:

# migrationclass CreateFoos < ActiveRecord::Migration[5.0]  def change    create_table :foos do |t|      t.integer :bar    end  endend# modelclass Foo < ApplicationRecordend# rake tasktask 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

Set pool to 1 in your config/database.yml and run the task. You will get an error. Set it to 2 - it will be just fine.

You can increase the number of threads in the pool and add at least that much promises for processing. You will consistently fail for database connection pool = number of threads in the thread pool and succeed if you add one more in config/database.yml.


Since you are defining the fixed thread pool to have one thread, I would assume that you are not achieving any kind of concurrency. Looking at your error it appears that the one available thread from the pool was busy for too long and caused the connection timeout exception.

When you altered your code implementation so that it did not contain a thread pool, the application was explicitly single threaded without the possibility of a connection timeout due to waiting on threads from a pool. Try increasing the size of your thread pool (perhaps to 3 or 5) and see if you are still getting that same exception.