One possible reason for the timeout is that connections do not return to ActiveRecord’s connection pool after use.
If you are not using ConnectionManagement or you want to try a solution right away, go to How do I release ActiveRecord connections?.
If you are using ConnectionManagement, you may be running Thin in threaded mode and thus not closing or returning to the pool any connections.
Keep reading for an explanation.
ActiveRecord 4.2 and Thin 1.6 threaded mode do not get along
There are two reasons why ActiveRecord’s ConnectionManagement does not release database connections under Thin’s threaded mode.
One reason is that ConnectionManagement releases only the one connection that is open for the current thread.
The other reason is that for each HTTP request, Thin executes the corresponding Sinatra route handler in one thread and applies ConnectionManagement in another thread.
To illustrate the situation, consider the following newsletter API.
To run the API, copy paste the contents in file newsletter.rb, give execution permissions, and execute newsletter.rb like so.
When the API is running, register subscribers by putting to path /subscribers/:email like so.
When we register a subscriber, the call stack printed by line newsletter.rb:19 is the following.
The following line of the call stack indicates that the execution of route handler for /subscribers/:email is preceded by a call to method ConnectionManagement.call.
We are using middleware ConnectionManagement because we asked for it in line newsletter.rb:15.
The code of ConnectionManagement in file connection_pool.rb is the following.
The route handler is executed during execution of line connection_pool.rb:653 and that is when a database connection is created for the current thread.
Line connnection_pool.rb:655 puportedly clears (releases) active connections, but only only for the current thread.
The reason is that the sequence of calls that starts in that line eventually releases a default connection identified by a thread id.
Consider the code for ActiveRecord::Base.clear_active_connections! in connection_pool.rb.
Line connection_pool.rb:552 calls ConnectionPool#release_connection for each existing thread.
The code for ConnectionPool#release_connection is the following.
The current connection id is the id of the current thread and is given by line connection_pool.rb:442.
Thus, ConnectionPool#release_connection releases the connection that has the same id as the current thread id. When you take from a given pool p a connection in one thread and try to release that connection in another thread by a call to p.release_connection, you will not release anything.
Given the way ActiveRecord::Base.clear_active_connections! works, you may expect that lines 653 and 655 of connection_pool.rb execute in the same thread.
They do not.
You may verify this fact by inspecting the thread id and call stack for those lines in the following way.
The registration of the subscriber produces the following output.
The thread ids and the call stacks are different, thus the call to ActiveRecord::Base.clear_active_connections! does not return the connection that was created in thread 70307221466660.
We reach the default maximum count of active connections after registering the fifth subscriber.
The API times out in subsequent requests.
To split or not to split hairs
I think that ActiveRecord’s ConnectionManagement and Thin should work together in threaded mode.
I can think of three approaches for reconciling them.
Approach 1: ActiveRecord’s ConnectionManagement releases connections for a specific thread.
However, reconciliation seems to be easier said than done for two reasons.
One is that after raising the problem to macournoyer, I am not sure that Thin is responsible for accomodating to ActiveRecord’s ConnectionManagement approach to releasing connections.
The other reason is that I am not sure that the recent elimination of ConnectionManagement from ActiveRecord takes the problem in consideration and thus raising the problem to ActiveRecord may be complicated.
How do I release ActiveRecord connections?
Whether Sinatra, ActiveRecord, and Thin reconcile or not, you will still want to clear active connections.
For Sinatra 1.4, ActiveRecord 4.2, and Thin 1.6 running in threaded mode, one way of releasing connections is calling ActiveRecord::Base.clear_active_connections! in an after filter, like so.