Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #2742 (closed defect: wontfix)

Opened 3 years ago

Last modified 2 years ago

allow_concurrency and associated database connection symantics are broken/baffling

Reported by: Jordan Husney <jordan@husney.com> Assigned to: David
Priority: normal Milestone: 1.1
Component: ActiveRecord Version: 0.12.1
Severity: normal Keywords: concurrency too many connections allow_concurrency
Cc: bitsweat

Description

Problem Background

I have an application that runs as a multi-threaded daemon using ActiveRecord on the back-end to aggregate data from multiple data sources into a single database. After investigating reports of this application failing and returning an exception containing the text "too many connections" I chased it up the call stack to discover that it was ActiveRecord's MySQL Adapter that was tossing the exception.

My application behaves in the following way:

  1. Initialize and use ActiveRecord:Base.establish_connection to configure and connect to the database.
  2. Service sockets to receive and parse messages.
  3. Call a function which instantiates an object type based on the ActiveRecord class, then:
    1. Set the AR object's variables to the data contained in the message.
    2. Save the object.
  4. Return; rinse, wash, and repeat.

Inferring from the fact that I was configuring ActiveRecord to connect to MySQL via a UNIX file-system socket and that I was only calling establish_connection once, I was surprised to find that I could be running out of connections at all. After searching and researching I found out that if allow_concurrency is set to true--which is the default if ActiveRecord::Base is instantiated outside of WEBrick, as according to this documentation--rails will and should establish one and only one connection for every class derived from ActiveRecord if and only if a connection does not already exist for a previously instantiated class or ancestor of that class, all the way up to ActiveRecord::Base itself. The problem is this does not seem to be the case.

Careful debugging has shown that AR is attempting to create a new database connection each and every time an ActiveRecord::Base derived class is being instantiated. Since database connections never close, it doesn't take too long before MySQL runs out of connections to offer and ultimately my application begins to fail.

Disabling concurrent connections in the following manner within my application initialization seems to act as a suitable work-around:

    ActiveRecord::Base.allow_concurrency = false
    ActiveRecord::Base.connection

Resolution Discussion

I would love to help fix this problem and provide a patch, but I don't have a clear understanding on what should be the correct behavior. Specifically:

  1. How and when should connections to the database be established when concurrency is enabled?
  2. When concurrency is enabled, how, when and should connections to the database ever close?

Final Notes

Thank you very much! I look forward to hearing how I may help!

Change History

11/06/05 08:17:42 changed by david

  • severity changed from major to normal.

1. Each thread should have its own connection. This will make sure that transactions don't step over each other. 1. When the thread is joined, the connection should be closed.

Do note that the current implementation gives 1 connection per thread to an endless amount of threads. So one needs to manage the threads. There's a patch in the queue (#2162) to add a connection pool, though, that would make this problem significantly less so.

11/06/05 14:56:09 changed by Jordan <jordan@husney.com>

Thanks for the reply!

One connection per thread makes a lot of sense here. I am going to have to investigate the XMLRPC/WEBrick source where is joins threads and match that up to the event handling code in Rails. If I have it right, either WEBrick is not joining threads when I think they should be joined (when it closes client connections) or Rails isn't actually closing the database connection at join-time.

In your esteemed opinion it is considered bad to have multiple threads "sharing" one connection handle? Or, when the handle is in use is there some sort of locking to keep the threads from stepping on each other? It's the latter, isn't it?

Is why the WEBrick server shipped with rails disables allow_concurrency?

Thank you again!

11/23/05 03:18:56 changed by bitsweat

  • cc set to bitsweat.
  • keywords changed from ActiveRecord concurrency too many connections allow_concurrency to concurrency too many connections allow_concurrency.
  • milestone set to 1.1.

The webrick server serializes web requests to avoid concurrency issues.

Most database drivers don't handle thread-safety for you. So we need to either synchronize access to the db connection (bad -- think of long-running transactions) or have threads request connections from and release them to a pool of available connections.

07/07/06 23:31:35 changed by bitsweat

  • status changed from new to closed.
  • resolution set to wontfix.

Please reopen with a patch and test cases if this remains a problem.

07/22/06 07:47:02 changed by anonymous

beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita beita

08/17/06 09:03:51 changed by anonymous