Framework: DB::reconnect isnt working but DB:purge is

Created on 25 May 2015  路  4Comments  路  Source: laravel/framework

not sure whats going here as the db area of laravel is like black magic to me, but im working on a multi tenancy app and use two main connections: tenants and tenant

when i create a new tenant i need to set the tenant db config as needed (prefix etc) and reconnect to the db before running migrations (all done without artisan calling the migrator via the oic container).

problem i have just found is that setting the config and then calling DB::reconnect('tenant') doesnt seem to work?

but setting the config and then calling DB::purge('tenant'); DOES work.

ive had a look and i see no reason other than something caching somewhere the connection as i assume both methods should achieve the same end result.

Most helpful comment

@jcalonso @sakibbuddy ...and for anyone else venturing into multi-tenancy/multi-database arrangements, this issue is still relevant in Laravel 5.7.* however it is not a bug.

What you are experiencing is the intended behavior as far as I can tell.

Neither of following methods remove connections from the connection cache here:

  • DB::connection
  • DB::reconnect
  • DB::disconnect

Therefore calling the likes of:

Config::set(
    'database.connections.YOUR_CONNECTION.database', 
    'YOUR_DATABASE'
);

...will have no effect until you call:

DB::purge($name)

...which will clear the $connections property within Illuminate\Database\DatabaseManager for the specified connection.

Then and only then will subsequent calls to connection and reconnect methods will make their way into the makeConnection method which retrieves the config for the connection in the current application state (i.e. your modifications via Config::set will be recognized at this point).

FYI: $name argument is optional when calling DB::purge and if not specified will default to purging the default connection only which can be a gotcha if you are using multiple connections for the same or multiple drivers.

Notes:

  • it doesn't matter whether you call Config::set before or after DB::purge($name), just so long as you set the value setting the connection
  • calling one of either DB::connection or DB::reconnect will work, so long as you have purged and set your config variable

Alternatives:

The alternative to setting config variables on the fly and additionally having to purge, connect or reconnect would be to extend the db singleton registered in the container in your AppServiceProvider or some other provider of your choosing:

class AppServiceProvider extends ServiceProvider
{
    /* ... */

    public function register()
    {
        $this->app['db']->extend('YOUR_CONNECTION', function ($config, $name) {

            if ( $condition) {
                $config['database'] = $database_name;
            }

            return $this->app['db.factory']->make($config, $name);

        });
    }   

    /* ... */
}

...there's some articles here and docs here on extending bindings.

Hope that helps...

All 4 comments

What laravel version? How can I replicate this?

Im facing the same issue in Laravel 5.1 and 5.3 (havent try 5.2). This is the code that replicate this behaviour:

<?php

use \Illuminate\Support\Facades\Config;
use \Illuminate\Support\Facades\DB;

class DbReconnectTest extends TestCase
{
    public function testDbReconnect()
    {
        Config::set('database.connections.mysql.database', 'tenant1');
        DB::reconnect('mysql');
        $connections = DB::getConnections();
        $this->assertEquals('tenant1', $connections['mysql']->getDatabaseName(), 'Initial reconnect works since it the default');

        Config::set('database.connections.mysql.database', 'tenant2');
        DB::reconnect('mysql');
        $connections = DB::getConnections();
        $this->assertEquals('tenant2', $connections['mysql']->getDatabaseName(), 'The actual reconnect to another db fails');

        DB::purge('mysql');
        DB::reconnect('mysql');
        $connections = DB::getConnections();
        $this->assertEquals('tenant2', $connections['mysql']->getDatabaseName(), 'If purged, it works');
    }
}


Output:

./vendor/bin/phpunit 
PHPUnit 5.5.3 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 78 ms, Memory: 10.50MB

There was 1 failure:

1) DbReconnectTest::testDbReconnect
The actual reconnect to another db fails
Failed asserting that two strings are equal.

--- Expected
+++ Actual
@@ @@
-'tenant2'
+'tenant1'

/Users/user/Desktop/laravelapp/tests/DbReconnectTest.php:18

FAILURES!
Tests: 1, Assertions: 2, Failures: 1.

I faced the same issue with Laravel version 5.3.31.

I had to use DB::purge() and then DB::connection() instead of DB::reconnect().

@jcalonso @sakibbuddy ...and for anyone else venturing into multi-tenancy/multi-database arrangements, this issue is still relevant in Laravel 5.7.* however it is not a bug.

What you are experiencing is the intended behavior as far as I can tell.

Neither of following methods remove connections from the connection cache here:

  • DB::connection
  • DB::reconnect
  • DB::disconnect

Therefore calling the likes of:

Config::set(
    'database.connections.YOUR_CONNECTION.database', 
    'YOUR_DATABASE'
);

...will have no effect until you call:

DB::purge($name)

...which will clear the $connections property within Illuminate\Database\DatabaseManager for the specified connection.

Then and only then will subsequent calls to connection and reconnect methods will make their way into the makeConnection method which retrieves the config for the connection in the current application state (i.e. your modifications via Config::set will be recognized at this point).

FYI: $name argument is optional when calling DB::purge and if not specified will default to purging the default connection only which can be a gotcha if you are using multiple connections for the same or multiple drivers.

Notes:

  • it doesn't matter whether you call Config::set before or after DB::purge($name), just so long as you set the value setting the connection
  • calling one of either DB::connection or DB::reconnect will work, so long as you have purged and set your config variable

Alternatives:

The alternative to setting config variables on the fly and additionally having to purge, connect or reconnect would be to extend the db singleton registered in the container in your AppServiceProvider or some other provider of your choosing:

class AppServiceProvider extends ServiceProvider
{
    /* ... */

    public function register()
    {
        $this->app['db']->extend('YOUR_CONNECTION', function ($config, $name) {

            if ( $condition) {
                $config['database'] = $database_name;
            }

            return $this->app['db.factory']->make($config, $name);

        });
    }   

    /* ... */
}

...there's some articles here and docs here on extending bindings.

Hope that helps...

Was this page helpful?
0 / 5 - 0 ratings

Related issues

YannPl picture YannPl  路  3Comments

PhiloNL picture PhiloNL  路  3Comments

klimentLambevski picture klimentLambevski  路  3Comments

shopblocks picture shopblocks  路  3Comments

CupOfTea696 picture CupOfTea696  路  3Comments