简体   繁体   中英

Laravel multi-tenant jobs

Running into a problem where jobs cannot connect to the database.

Invalid catalog name: 1046 No database selected

I need to set the account in the job so I have an extended class to make sure the account is sent with the job so that I can ensure the database can connect to the correct database.

<?php

namespace App\Jobs;

use Illuminate\Support\Facades\DB;

abstract class Job
{
    protected $account;

    public function start()
    {
        // runs when creating the job, so the config holds the correct value
        $this->account = config('database.connections.tenant.database');
    }

    public function handle()
    {
        // since the handle function runs outside of setting the job 
        // the database is no longer set in the config
        config()->set('database.connections.tenant.database', $this->account);
        // try to force it to reconnect incase it already did for some reason.
        DB::reconnect();
    }
}

This is my current version I am playing around with, variations seem to not affect it. I basically run start in the constructor and then make sure it runs the parent handle in the job so that it bootstraps the proper db configuration.

The end result I am looking for is it to set the tenant database as account and when its running the job it uses that database for all queries.

Found out a way around this. Its not pretty but based on what I can see laravels queues just don't really handle this sort of thing well.

First I removed the override for the handle function, all I really needed was to make sure the account the queue needed to run on was available on the Job class.

abstract class Job
{
    protected $account;

    public function start()
    {
        // runs when creating the job, so the config holds the correct value
        $this->account = config('database.connections.tenant.database');
    }
}

Next I moved the switch to the correct tenant database to the AppServiceProvider in the boot method.

Event::listen(JobProcessing::class, function ($event) {
   if ($payload = $event->job->payload()) {
      preg_match('/"account";s:[0-9]+:"(.*?)"/', $payload['data']['command'], $matches);
      if (count($matches)) {
         if (isset($matches[1])) {
            config()->set('database.connections.tenant.database', $matches[1]);
            config()->set('database.default', 'tenant');
         }
      }
   }
});

What i did here is look into the serialize object for the account with some regex. Might be improvements to be made here but so far works in testing. It then sets the correct database once it confirms the account.

The reason I had to go this far is that the Job does queries when the job itself is serialized, so in order to pass the account it needed to be done before its serialized.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM