I have this issue too.
Help Needed with Tenant-Aware Jobs in stancl/tenancy Multi-Database Setup
Hi everyone,
I'm using the stancl/tenancy package to handle a multi-tenant, multi-database application in Laravel. Everything is working well except for the job dispatching and queue system.
Since I have only one global queue worker for my application and it can only work on one queue, I need all tenant jobs to be saved in the central database in the jobs table. When the job is executed, the tenant context must be initialized at that moment.
I've followed this guide and after multiple attempts, I still can't get it to work: tenancyforlaravel.com/docs/v3/queues/
I also tried creating a tenant template connection in the database, but without success.
composer.json
{
"require": {
"php": "^8.1.0",
"laravel/framework": "^10.0",
"stancl/tenancy": "^3.8",
// My other packages...
}
}
Here are the relevant configurations:
config/tenancy.php:
'bootstrappers' => [
Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper::class,
Stancl\Tenancy\Bootstrappers\CacheTenancyBootstrapper::class,
Stancl\Tenancy\Bootstrappers\FilesystemTenancyBootstrapper::class,
Stancl\Tenancy\Bootstrappers\QueueTenancyBootstrapper::class,
// Stancl\Tenancy\Bootstrappers\RedisTenancyBootstrapper::class, // Note: phpredis is needed
],
config/queue.php:
'database' => [
'connection' => 'mysql',
'driver' => 'database',
'table' => 'jobs',
'queue' => 'default',
'retry_after' => 90,
'after_commit' => false,
],
'central' => [
'connection' => 'mysql',
'driver' => 'database',
'table' => 'jobs',
'queue' => 'default',
'retry_after' => 90,
'after_commit' => false,
'central' => true,
],
Job Dispatching: Here is how I'm dispatching the job from within a tenant context:
namespace App\Http\Controllers\Tenant\CustomerManagement;
use App\Http\Controllers\Controller;
use App\Jobs\Tenant\CustomerManagement\GenerateReceiptJob;
use Illuminate\Http\Request;
use App\Models\Purchase;
class PurchaseController extends Controller
{
public function generateReceipt(Request $request, Purchase $purchase)
{
GenerateReceiptJob::dispatch($purchase)->onConnection('central');
}
}
<?php
namespace App\Jobs\Tenant\CustomerManagement;
use App\Models\Purchase;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class GenerateReceiptJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(protected Purchase $purchase){}
public function handle()
{
// TODO: Generate PDF receipt for purchase...
}
}
InvalidArgumentException: Database connection [tenant] not configured. in C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Database\DatabaseManager.php:196
Stack trace:
#0 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Database\DatabaseManager.php(159): Illuminate\Database\DatabaseManager->configuration('tenant')
#1 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Database\DatabaseManager.php(101): Illuminate\Database\DatabaseManager->makeConnection('tenant')
#2 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php(1819): Illuminate\Database\DatabaseManager->connection('tenant')
#3 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php(1785): Illuminate\Database\Eloquent\Model::resolveConnection('tenant')
#4 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php(1576): Illuminate\Database\Eloquent\Model->getConnection()
#5 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php(1495): Illuminate\Database\Eloquent\Model->newBaseQueryBuilder()
#6 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php(1531): Illuminate\Database\Eloquent\Model->newModelQuery()
#7 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php(1555): Illuminate\Database\Eloquent\Model->newQueryWithoutScopes()
#8 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Queue\SerializesAndRestoresModelIdentifiers.php(121): Illuminate\Database\Eloquent\Model->newQueryForRestoration(15834)
#9 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Queue\SerializesAndRestoresModelIdentifiers.php(108): App\Jobs\GenerateReceiptJob->getQueryForModelRestoration(Object(App\Models\Purchase), 15834)
#10 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Queue\SerializesAndRestoresModelIdentifiers.php(62): App\Jobs\GenerateReceiptJob->restoreModel(Object(Illuminate\Contracts\Database\ModelIdentifier))
#11 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Queue\SerializesModels.php(93): App\Jobs\GenerateReceiptJob->getRestoredPropertyValue(Object(Illuminate\Contracts\Database\ModelIdentifier))
#12 [internal function]: App\Jobs\GenerateReceiptJob->__unserialize(Array)
#13 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Queue\CallQueuedHandler.php(97): unserialize('O:27:"App\Jobs\...')
#14 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Queue\CallQueuedHandler.php(60): Illuminate\Queue\CallQueuedHandler->getCommand(Array)
#15 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Queue\Jobs\Job.php(102): Illuminate\Queue\CallQueuedHandler->call(Object(Illuminate\Queue\Jobs\DatabaseJob), Array)
#16 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Queue\Worker.php(439): Illuminate\Queue\Jobs\Job->fire()
#17 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Queue\Worker.php(389): Illuminate\Queue\Worker->process('central', Object(Illuminate\Queue\Jobs\DatabaseJob), Object(Illuminate\Queue\WorkerOptions))
#18 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Queue\Worker.php(333): Illuminate\Queue\Worker->runJob(Object(Illuminate\Queue\Jobs\DatabaseJob), 'central', Object(Illuminate\Queue\WorkerOptions))
#19 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Queue\Console\WorkCommand.php(138): Illuminate\Queue\Worker->runNextJob('central', 'default', Object(Illuminate\Queue\WorkerOptions))
#20 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Queue\Console\WorkCommand.php(121): Illuminate\Queue\Console\WorkCommand->runWorker('central', 'default')
#21 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(36): Illuminate\Queue\Console\WorkCommand->handle()
#22 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Container\Util.php(41): Illuminate\Container\BoundMethod::Illuminate\Container\{closure}()
#23 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(93): Illuminate\Container\Util::unwrapIfClosure(Object(Closure))
#24 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php(37): Illuminate\Container\BoundMethod::callBoundMethod(Object(Illuminate\Foundation\Application), Array, Object(Closure))
#25 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Container\Container.php(662): Illuminate\Container\BoundMethod::call(Object(Illuminate\Foundation\Application), Array, Array, NULL)
#26 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Console\Command.php(211): Illuminate\Container\Container->call(Array)
#27 C:\xampp\htdocs\laravel\projects\my-project\vendor\symfony\console\Command\Command.php(326): Illuminate\Console\Command->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle))
#28 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Console\Command.php(181): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle))
#29 C:\xampp\htdocs\laravel\projects\my-project\vendor\symfony\console\Application.php(1096): Illuminate\Console\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#30 C:\xampp\htdocs\laravel\projects\my-project\vendor\symfony\console\Application.php(324): Symfony\Component\Console\Application->doRunCommand(Object(Illuminate\Queue\Console\WorkCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#31 C:\xampp\htdocs\laravel\projects\my-project\vendor\symfony\console\Application.php(175): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#32 C:\xampp\htdocs\laravel\projects\my-project\vendor\laravel\framework\src\Illuminate\Foundation\Console\Kernel.php(201): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#33 C:\xampp\htdocs\laravel\projects\my-project\artisan(37): Illuminate\Foundation\Console\Kernel->handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#34 {main}
Despite following these steps, the jobs are not executed within the tenant context. They are processed, but they do not seem to be aware of the tenant they belong to.
Has anyone faced a similar issue or can suggest what might be going wrong? Any help or pointers would be greatly appreciated!
Thank you in advance!
Please or to participate in this conversation.