Yesterday someone commented in my article on Sequel, where I compare
it with AR in some aspects, including pool management:
http://rosenfeld.herokuapp.com/en/articles/ruby-rails/2013-12-18-sequel-is-awesome-and-much-better-than-activerecord
To answer his comments I decided to first take a glance at the
current (4.1.1) implementation of how the connections' pool work in
Rails with AR.
After our discussion in the comments, when I was about to sleep, I
was thinking more about this subject and decided it might worth
bringing some ideas to you, in case you'd be interested on them...
Basically, ActiveRecord currently relies on delegating the
connection pool management to the user. Most users don't realize it
because they don't usually spawn new threads from the main request
thread and there's an AR middleware that's automatically integrated
to Rails that will checkin the connection back to the pool in the
end of the request. Since the connection id is set in a thread local
that means the Rack middleware can only checkin the connection used
in the main request thread. Here's some example to illustrate:
class MainController
def index
Thread.start{ Post.count }
head :ok
end
end
Assuming the default pool size (5), running this action 6 times will
fail currently:
ab -n 6 -c 1
http://localhost:3000/main/index
This is not anything new and Aaron Patterson has already touched
this subject long ago, in 2011:
http://tenderlovemaking.com/2011/10/20/connection-management-in-activerecord.html
In a side note, yesterday I learned about an interesting project to
set a common API for job libraries that is intended to be merged to
Rails at some point:
https://github.com/rails/activejob
The default adapter (inline) implements an "enqueue_at" method that
will spawn a new thread:
https://github.com/rails/activejob/blob/master/lib/active_job/queue_adapters/inline_adapter.rb#L9-L18
So, calling enqueue_at for a job using the default adapter will
share the same problems of the implementation above.
Then I was thinking that most of AR API could be implemented in a
smarter way, so that this wouldn't be a problem. That means calling
"with_connection" behind the scenes whenever they need a connection.
Also, even "execute" could be implemented this way. Instead of
checking out a connection by calling AR::Base.connection, it could
simply return a proxy. If you really want to checkout and reserve
that connection you could call "connection.lock" for instance and
then the user would know that they must ensure "unlock" is called
after it's done. But otherwise, calling "execute" would perform the
query under a "with_connection" block, checking the connection in
back to the pool after running the SQL statement.
I'm just suggesting the idea in case someone might be interested in
coming up with a PoC for this in case the core team agrees with the
suggested approach (it introduces a bit of backward
incompatibilities). I don't plan to work on this, specially because
I don't use AR myself, but maybe a better automatic handling of
connections in the pool might be of interest to most AR users...
Cheers,
Rodrigo.