I understand that it should close the connection, since it unsets the connection object, but my crude but straightforward test suggest that it doesn't.
In your local set-up, set the MAX_CONNECTIONS to a very low number (5 is good). Then create a good 10 empty DBs in your MySQL instance. To keep them easy to remember, manipulate and then delete, call them a1, a2, a3, etc.
Now, if you circumvent the drupal API. And just open a bunch of connections to these DB's using just the PDO. It should look like this:
for($i=1; $i<=10; $i++) {
$dbh[$i] = new PDO('mysql:host=localhost;dbname=a' . $i, YOUR_DB_USER_HERE, YOUR_DB_PASS_HERE);
// use the connection here
$dbh[$i]->exec("SHOW tables");
}
Throw that into a devel window (replace DB creds with your own (privileged) DB user) and your site should crash from too many connections. That makes sense.
But add one more line to this, one that unsets the connection object, and you are safe. No PDO exception for too many connections.
for($i=1; $i<=10; $i++) {
$dbh[$i] = new PDO('mysql:host=localhost;dbname=a' . $i, YOUR_DB_USER_HERE, YOUR_DB_PASS_HERE);
// use the connection here
$dbh[$i]->exec("SHOW tables");
$dbh[$i] = NULL; // close it
}
Which all makes sense. From PHP.net (http://php.net/manual/en/pdo.connections.php)
To close the connection, you need to destroy the object by ensuring that all remaining references to it are deleted--you do this by assigning NULL to the variable that holds the object.
But if we try this in Drupal, however, the connections simply don't close.
First, add the new dummy DBs to drupal's global $database variable. (I like to just throw the following into settings.php)
for($i=1; $i<=10; $i++){
$databases['a' . $i]['default'] =
array (
'database' => 'a' . $i,
'username' => 'root',
'password' => 'root',
'host' => 'localhost',
'port' => '',
'driver' => 'mysql',
'prefix' => '',
);
}
Then insert the following into a php window.
for($i=1; $i<=10; $i++) {
db_set_active('a' . $i);
db_query("SHOW TABLES");
db_set_active();
}
Your site will again crash, which makes sense. But if we try to explicitly close the connections as we did above with just the PDO ....
for($i=1; $i<=10; $i++) {
db_set_active('a' . $i);
db_query("SHOW TABLES");
db_set_active();
Database::closeConnection(NULL, 'a' . $i);
}
You will still reach too many connections. I'm certainly no expert in PHP5 object management, but it seems that the key line of closeConnection
unset(self::$connections[$key]);
does not completely delete every reference to the connection object. I don't know if it's because another method (openConnection) actually creates the object, or if it's because we're using the Database abstract class to do all this.
Comments
Comment #1
tim.plunkettMoving to D8, this doesn't seem like a major bug
Comment #2
Anonymous (not verified) commentedFrom the supplied link in the node we also see:
As well persistent connections is a configurable item - see http://dev.mysql.com/doc/refman/5.1/en/apis-php-mysql.setup.html for more.
Comment #3
sunBumping to critical, since this bug is causing random test failures on HEAD currently.
TestBase::tearDown() calls Database::removeConnection(), which is supposed to also close the connection.
Manually injecting an additional Database::closeConnection() does not change the situation.
Monitoring the result of
SHOW PROCESSLISTduring a test run shows that new connections are stacking up for every test method that is executed in a test case.Comment #4
sunActually, this is a duplicate of #843114: DatabaseConnection::__construct() and DatabaseConnection_mysql::__construct() leaks $this (Too many connections)
Comment #5
crookednumber commentedThanks, Sun, for your input -- and the dupe link. Most helpful.
Comment #6
crookednumber commentedFor anyone coming to this thread with a similar problem, read the ticket that this one dupes #843114: DatabaseConnection::__construct() and DatabaseConnection_mysql::__construct() leaks $this (Too many connections)
TL;DR: You can get closeConnection() to actually work if you follow the hack outlined by quicksketch in this comment: http://drupal.org/node/843114#comment-6075722
Applied to the code sample above, it would look something like:
Yeah, it'll iterate over the loop a little more slowly. But it shouldn't crash from too many connections.