Search code examples
laraveljobs

Laravel jobs without overlap in sync mode


For example I have a job called TestQueueJob which is proccessing using database queue driver, inside the job I call another job called TestSyncJob which is running in sync mode (TestSyncJob::dispatchSync()). The issue I am facing now is I need TestSyncJob to not overlap ever and when I have 2 or more TestQueueJob jobs running at the same time the issues come. I have tried to implement the job middleware using WithoutOverlapping class but in sync mode it just skippes the TestSyncJob job and I need it to wait until the other TestSyncJob execution is finished and to continue.

TestQueueJob::dispatch(); //running using database queue driver, there are > 1 numprocs in supervisor for the queue
class TestQueueJob implements ShouldQueue
{
    public function handle(): void
    {
        //some code
        TestSyncJob::dispatchSync(); //should wait until other TestSyncJob is finished and then run
        //some code
    }
}

use Illuminate\Queue\Middleware\WithoutOverlapping;

class TestSyncJob implements ShouldQueue
{
    public function handle(): void
    {
        \Log::info('Sync job started!');
        sleep(10);
        \Log::info('Sync job finished!');
    }

    public function middleware()
    {
        return [new WithoutOverlapping(1)];
    }
}

Solution

  • I have ended up extending the existing WithoutOverlapping middleware. It's now waiting for other jobs to finish and proceeds the executing of a job instead of skipping it in a "sync" queue mode.

    class CustomWithoutOverlapping extends WithoutOverlapping
    {
        public function handle($job, $next)
        {
            $lock = Container::getInstance()->make(Cache::class)->lock(
                $this->getLockKey($job), $this->expiresAfter
            );
    
            if ($lock->get()) {
                try {
                    $next($job);
                } finally {
                    $lock->release();
                }
            //START of new code
            } elseif ($job->connection === 'sync') {
                do {
                    sleep(1);
                } while (!$lock->get());
    
                try {
                    $next($job);
                } finally {
                    $lock->release();
                }
            //END of new code
            } elseif (!is_null($this->releaseAfter)) {
                $job->release($this->releaseAfter);
            }
        }
    }