Search code examples
laravelmulti-tenant

Laravel Multi-Tenant (multi-schema) fails with queues


So I've created a large chunk of an api in laravel that is multitenant. There's a jwt issued that comes with a user id and a tenant id. On authenticated requests, I use the tenant in the token to overwrite the postgres schema in the database config. It works fine for all requests, until I get to the queue. I'm realizing now that when I queue a job, event, notification etc it's looking in the public schema (I should have expected that). However, after a full day of digging through code and documentation, I don't see any way to set the schema for a queued job. I have ways of getting around it (pass it as a parameter and set the schema in the constructor) but it's repetitive and also I haven't found a way to do so in a notification (if I'm notifying a user, it'll fail looking up the proper user at _wakeup()). I'm not sure what code I could share to help figure this out, as it's less an implementation problem and more of a "I have no idea how to implement" problem.

I know I could likely create a job that overwrites the schema and THEN synchronously sends the notification but again, I'm falling into very hacky territory.


Solution

  • I implemented a multi-tenant application and ran into the same problem. Sadly there is no way to hook into a job when it is dispatched.

    I created a simple trait to prevent cluttering all jobs with a tenant parameter:

    trait TenantAware
    {
        protected $tenant;
    
        public static function dispatchForTenant(Tenant $tenant, ...$args)
        {
            return new PendingDispatch(
                (new static(...$args))->withTenant($tenant)
            );
        }
    
        public function withTenant(Tenant $tenant)
        {
            $this->tenant = $tenant;
    
            return $this;
        }
    }
    

    Then you can use job events to set your schema:

    Queue::before(function (JobProcessing $event) {
        // TODO: Retrieve tenant from job payload and set PostgreSQL schema.
    });
    
    Queue::after(function (JobProcessing $event) {
        // TODO: Reset PostgreSQL schema.
    });
    

    And dispatch with:

    SendInvoiceExampleJob::dispatchForTenant($tenant, $user, invoice);