If the connection's PDO gets changed via DB::reconnect() the doctrineConnection property does not get updated and still holds the old PDO information. Queries using the doctrine connection will execute using wrong settings.
Assuming two databases with identical structure ('table' containing a column 'column1') and :
Config::set('database.connections.mysql.database', 'firstDb');
DB::reconnect();
Schema::table('table', function (Blueprint $table) {
$table->renameColumn('column1', 'column2');
});
Config::set('database.connections.mysql.database', 'secondDb');
DB::reconnect();
Schema::table('table', function (Blueprint $table) {
$table->renameColumn('column1', 'column2');
});
This results in:
There is no column with name 'column1' on table 'table'.
Explanation:
While the first schema change works correctly, the second one wrongly still uses the old PDO information (ie running on db 'firstDb') and sees there is no 'column1' on it because it already got renamed. If the PDO change would have been propagated to the doctrineConnection it would see that the 'column1' still exists on 'secondDb'.
* Note: * This only works if database.connections.mysql.database in the initial database.php config file is set to _null_, although I am not sure why.
reset doctrineConnection as well whenever a reconnect or disconnect occurs:
\App\Database\MySqlConnectionWithPDOFix:
<?php
namespace App\Database;
use Illuminate\Database\MySqlConnection;
class MySqlConnectionWithPDOFix extends MySqlConnection {
public function reconnect() {
$this->doctrineConnection = null;
return parent::reconnect();
}
public function disconnect() {
$this->doctrineConnection = null;
parent::disconnect();
}
}
\App\Providers\AppServiceProvider:
Connection::resolverFor('mysql', function($connection, $database, $prefix, $config) {
return new MySqlConnectionWithPDOFix($connection, $database, $prefix, $config);
});
For suggestions and feature requests please use the https://github.com/laravel/internals repo.
How is this a suggestion or a feature request?
If some queries reuse an old database connection after setting config to another database and reconnecting there is clearly something wrong?
Facing the same problem and can't find the solution anywhere.
You can set a custom reconnector for your connection to reset the doctrineConnection property:
/** @var \Illuminate\Database\Connection $connection */
$connection->setReconnector(function(\Illuminate\Database\Connection $connection) use ($databaseManager) {
$reflectionProperty = new \ReflectionProperty($connection, 'doctrineConnection');
$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($connection, null);
/** @var \Illuminate\Database\DatabaseManager $databaseManager */
$databaseManager->reconnect($connection->getName());
});
I have no idea where to put this in the Laravel framework code, because I am using Illuminate/Database as a separate module in my project (so no Laravel Framework in the project).
DB::disconnect() seems to have the same issue. When using DB::disconnect() standalone the doctrine connection stays open and is not reset. Manually calling DB::connection()->getDoctrineConnection()->close(); before DB::disconnect() seems to fix this.
[5.8] Still an issue, disconnect does not remove the doctineConnection and this causes a build up of database connections.
We have a RabbitMQ queue processor which connects to many different databases depending on the message. We disconnect the connection once the task has been complete assuming the DB connection will be closed, however after a while our process breaks due to too many database connections being open.
Following the advice of @calvinbaart works, I can see our connection counts dropping as we would expect. The PDO object passed into the DoctrineConnection object is not being nullified when disconnect is called on the Connection object.
Even using DB::purge which unsets the connection from the DatabaseManager cache, does not resolve the database build up. It seems that removing the Connection doesn't remove the PDO connection... which makes me believe this issue is due to the events or query logging keeping a copy of the connection. Even though the local PDO is nullified the DoctrineConnection copy of the PDO persists somewhere on an event queue.
This issue is easy to recreate using MySQL, simply create many database connections in the config. Count the rows returned from the process list to see your baseline.
DB::select('SHOW PROCESSLIST')->count()
Disconnect or purge those connections and count the rows from the process list again. They should reduce in number, but they do not.
Most helpful comment
DB::disconnect() seems to have the same issue. When using DB::disconnect() standalone the doctrine connection stays open and is not reset. Manually calling
DB::connection()->getDoctrineConnection()->close();before DB::disconnect() seems to fix this.