Hi Schuck, that was indeed a smart trick to associate each connection
with is owner thread and check whether they are alive when there's
apparently no available connection. In that case the reaper is run and
could free up those connections when the thread is dead. I liked it :)
Simple and effective for most use cases.
In theory even the middleware would no longer be strictly required,
although it could add a bit of performance by avoiding the reaper to run
everytime...
Congrats to Mathew :)
But there's still another use case where a smarter implementation would
help.
Consider you spawn a new thread and this thread will perform a request
to the database. After it's done it will start some processing that
could take quite a lot of time but that will no longer need to perform
any statements in the database until the long processing is finished.
Something like this:
def my_action
Thread.start do
posts = Post.all.to_a
do_long_time_processing_with posts
Post.update processed: true
end
end
In this case, if someone needs a connection while the thread is
processing some data the connection would still be marked as in use even
though it's idle most of the time. You could certainly work around it by
issuing a close in the connection after Post.all.to_a, but it would be
better if this was handled in a transparent way.
Specially because it's not always obvious that this is the case. A
better management of the connections could decrease the need for a
bigger pool size.
That's why I think that it would be a good thing if the connections were
not collected only in the end of the request (or thread) cycle but
rather just after the statement is run. Unless the user explicitly wants
to lock the connection in case they are creating temporary tables valid
for the duration of the connection, or something like this...
But it's certainly good to know about this change for the upcoming Rails
4.2.
Thanks for sharing.
Rodrigo.
On 30-05-2014 12:06, T.J. Schuck wrote:
Specifically regarding a Thread outside of the main request thread
holding onto a connection, you might be interested in
https://github.com/rails/rails/pull/14360 about more intelligently
reaping connections held onto by dead threads.
Unfortunately, the comments on that PR say it will not be backported
to 4.1 and will only be included in 4.2 :(
On Fri, May 30, 2014 at 9:10 AM, Rodrigo Rosenfeld Rosas
<[email protected] <mailto:[email protected]>> wrote:
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.
--
You received this message because you are subscribed to the Google
Groups "Ruby on Rails: Core" group.
To unsubscribe from this group and stop receiving emails from it,
send an email to [email protected]
<mailto:[email protected]>.
To post to this group, send email to
[email protected]
<mailto:[email protected]>.
Visit this group at http://groups.google.com/group/rubyonrails-core.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google
Groups "Ruby on Rails: Core" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to [email protected]
<mailto:[email protected]>.
To post to this group, send email to [email protected]
<mailto:[email protected]>.
Visit this group at http://groups.google.com/group/rubyonrails-core.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "Ruby on
Rails: Core" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/rubyonrails-core.
For more options, visit https://groups.google.com/d/optout.