spatie / laravel-multitenancy

Make your Laravel app usable by multiple tenants
https://spatie.be/docs/laravel-multitenancy
MIT License
1.11k stars 153 forks source link

Switching tenants in the jobs queue #558

Closed TheAdnan closed 1 month ago

TheAdnan commented 1 month ago

So I have a problem with running the queue worker and the tenants. I have two queues, one is the default php artisan queue:work and the other is php artisan queue:work --queue=somethingElse. When doing a certain action on the app, Job1 would be dispatched to the default queue, and another job Job2 would then be dispatched to the other queue. When I start the queue workers and I'm dispatching the job from tenant1, everything works fine. As soon as I dispatch another job from tenant2, all following jobs would fail (for both tenants) with the following error:

Error: Call to a member function prepare() on null in /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php:368

I'm using Laravel v8, and v3 of the laravel-multitenancy package

Here are my config files for context:

    'connections' => [

            'mysql' => [
                        'driver' => 'mysql',
                        'host' => env('DB_HOST', '127.0.0.1'),
                        'port' => env('DB_PORT', '3306'),
                        'database' => null,
                        'username' => env('DB_USERNAME', 'forge'),
                        'password' => env('DB_PASSWORD', '')
                    ],
             'landlord' => [
                        'driver' => 'mysql',
                        'host' => env('DB_HOST', '127.0.0.1'),
                        'port' => env('DB_PORT', '3306'),
                        'database' => env('DB_LANDLORD_DATABASE', 'forge'),
                        'username' => env('DB_LANDLORD_USERNAME', 'forge'),
                        'password' => env('DB_LANDLORD_PASSWORD', '')
                    ],
         ]            
    'connections' => [
           'database' => [
            'driver' => 'database',
            'table' => 'jobs',
            'queue' => 'default',
            'retry_after' => 90,
            'connection' => 'landlord',
        ],
    ]            
   return [
    'tenant_finder' => \Spatie\Multitenancy\TenantFinder\DomainTenantFinder::class,

    'tenant_artisan_search_fields' => [
        'id',
    ],

    'switch_tenant_tasks' => [
         \Spatie\Multitenancy\Tasks\SwitchTenantDatabaseTask::class,
    ],

    'tenant_model' => \App\Models\MyTenant::class,

    'queues_are_tenant_aware_by_default' => true,

    'tenant_database_connection_name' => 'mysql',

    'landlord_database_connection_name' => 'landlord',

    'current_tenant_container_key' => 'currentTenant',

    'shared_routes_cache' => false,

    'actions' => [
        'make_tenant_current_action' => MakeTenantCurrentAction::class,
        'forget_current_tenant_action' => ForgetCurrentTenantAction::class,
        'make_queue_tenant_aware_action' => MakeQueueTenantAwareAction::class,
        'migrate_tenant' => MigrateTenantAction::class,
    ],

    'queueable_to_job' => [
        SendQueuedMailable::class => 'mailable',
        SendQueuedNotifications::class => 'notification',
        CallQueuedClosure::class => 'closure',
        CallQueuedListener::class => 'class',
        BroadcastEvent::class => 'event',
    ],

    'tenant_aware_jobs' => [
        // ...
    ],

    'not_tenant_aware_jobs' => [
        // ...
    ],
]; 
MattWiseParking commented 1 month ago

@TheAdnan Can you copy and paste the job which you are having problems with please?

TheAdnan commented 1 month ago

@TheAdnan Can you copy and paste the job which you are having problems with please?

Well turns out the error is with Laravel Pennant, when checking if an instance has a feature enabled. I think I need to adjust the functionality to check if a feature flag is enabled/disabled for a tenant instead.

MattWiseParking commented 1 month ago

change your config line from

'tenant_database_connection_name' => 'mysql',

to this:

'tenant_database_connection_name' => null,

then add a connection named tenant as this will be automatically used. your tenant connection should look something like this:

'tenant' => [
            'driver' => 'mysql',
            'url' => env('DATABASE_URL'),
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => null,
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'prefix_indexes' => true,
            'strict' => true,
            'engine' => null,
            'options' => extension_loaded('pdo_mysql') ? array_filter([
                PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
            ]) : [],
        ],

This will allow the system to automatically set the database based on the tenant