-
Notifications
You must be signed in to change notification settings - Fork 21.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Active Record locks connections to threads that checked them out #37092
Comments
I can confirm the test fails on stable-4-2, stable-5-0, stable-5-1 and stable-5-2 branches. |
Reading the documentation of ConnectionPool it looks like connections are cached in def connection
@thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
end I guess creating connections is expensive and you don't want to delete the query cache:
rails/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb Line 393 in 01cc0c1
|
Yes, connections should definitely stay cached. But they don't need to stay assigned to any thread, they can be made available "for grabs" by any thread. |
This issue has been automatically marked as stale because it has not been commented on for at least three months. |
The original script still reproduces the failure on |
I encountered the same issue and had to explicitely use Just saw that issue, so to give my 2 cents, I find it really counter-intuitive since that's not how a standard pool works. I guess the current design may be related to some threaded cache in AR. |
This issue has been automatically marked as stale because it has not been commented on for at least three months. |
I've run into this exact issue just now and would love to know the current thinking on this matter. Does the Fiber-safe ticket shown above take this issue and run with it? I'll need to give it a better read... |
Steps to reproduce
Expected behavior
I expected the above code to complete without errors.
The connection pool is intentionally smaller than the number of threads, but since we're executing cheap queries, I expected connections to be immediately checked back into the pool, so that other threads can use them.
Actual behavior
I get connection timeout from 6 threads:
This is because, once Active Record checks out a connection, it locks it to the thread that checked it out, so only that thread can continue to use it. Once the main thread + 4 other threads checked out their 5 connections, the other 6 threads didn't have any connections available.
Current workarounds
Historically, Active Record users have been recommended to either
AR::Base.clear_active_connections!
AR::Base.connection_pool.with_connection { ... }
In Rails, this has been managed automatically. Originally, there was a
ConnectionManagement
middleware that calledAR::Base.clear_active_connections!
after each request, releasing locked connections back into the pool. Today, this is surprisingly done by AR::QueryCache, which makes it appear as if though this functionality is only needed for query caching, when in fact it affects Active Record usage in general.In other web frameworks such as Sinatra, you need to call manually
AR::Base.clear_active_connections!
after each request. In background jobs, it's recommended to useAR::Base.connection_pool.with_connection { ... }
.Rationale
I believe Active Record's connection pool should automatically check connections back into the pool after queries are finished, available for any other thread to use it. Basically, what
AR::Base.connection_pool.with_connection { ... }
does.This is how Sequel's connection pool works. If we run the same script using Sequel, it will complete without errors:
I believe this change would reduce the chance of people running out of connections. For example, they wouldn't need to remember to use
ActiveRecord::Base.connection_pool.with_connection { ... }
in background jobs. Yes, users should configure sufficiently large pools, but maybe at some point they start using threads inside background jobs, and don't realize that this requires them to increase the pool size even more.I also believe this could reduce the load on the database, because it would allow using the smaller number of connections in certain cases. A pool of 50 background workers could theoretically use only 10 connections if the queries are fast.
System configuration
Rails version: master, 6.0.0, and probably any other version before
Ruby version: 2.6.0
The text was updated successfully, but these errors were encountered: