From 72306bebce1a5c41f324b86045ed6dc87a5f3129 Mon Sep 17 00:00:00 2001 From: Paras Malhotra Date: Wed, 18 Nov 2020 00:52:02 +0530 Subject: [PATCH 1/2] [8.x] Add option to release unique job locks before processing --- src/Illuminate/Queue/CallQueuedHandler.php | 4 ++++ tests/Integration/Queue/UniqueJobTest.php | 26 ++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/Illuminate/Queue/CallQueuedHandler.php b/src/Illuminate/Queue/CallQueuedHandler.php index 8647e39bc758..61126c775834 100644 --- a/src/Illuminate/Queue/CallQueuedHandler.php +++ b/src/Illuminate/Queue/CallQueuedHandler.php @@ -59,6 +59,10 @@ public function call(Job $job, array $data) return $this->handleModelNotFound($job, $e); } + if ($command->uniqueUntilStart ?? false) { + $this->ensureUniqueJobLockIsReleased($command); + } + $this->dispatchThroughMiddleware($job, $command); if (! $job->isReleased()) { diff --git a/tests/Integration/Queue/UniqueJobTest.php b/tests/Integration/Queue/UniqueJobTest.php index a6f2c1a5143b..374d2f7efce8 100644 --- a/tests/Integration/Queue/UniqueJobTest.php +++ b/tests/Integration/Queue/UniqueJobTest.php @@ -143,6 +143,23 @@ public function testLockIsNotReleasedForJobReleases() $this->assertTrue($this->app->get(Cache::class)->lock($this->getLockKey($job), 10)->get()); } + public function testLockCanBeReleasedBeforeProcessing() + { + UniqueUntilStartTestJob::$handled = false; + + dispatch($job = new UniqueUntilStartTestJob); + + $this->assertFalse($this->app->get(Cache::class)->lock($this->getLockKey($job), 10)->get()); + + $this->artisan('queue:work', [ + 'connection' => 'database', + '--once' => true, + ]); + + $this->assertTrue($job::$handled); + $this->assertTrue($this->app->get(Cache::class)->lock($this->getLockKey($job), 10)->get()); + } + protected function getLockKey($job) { return 'laravel_unique_job:'.(is_string($job) ? $job : get_class($job)); @@ -197,3 +214,12 @@ class UniqueTestRetryJob extends UniqueTestFailJob public $connection = 'database'; } + +class UniqueUntilStartTestJob extends UniqueTestJob +{ + public $tries = 2; + + public $connection = 'database'; + + public $uniqueUntilStart = true; +} From 2a6e9d32277f7083109f2ecceba7840de93760df Mon Sep 17 00:00:00 2001 From: Paras Malhotra Date: Wed, 18 Nov 2020 12:37:15 +0530 Subject: [PATCH 2/2] Ensure unique job lock is released only once in lifecycle --- src/Illuminate/Queue/CallQueuedHandler.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Queue/CallQueuedHandler.php b/src/Illuminate/Queue/CallQueuedHandler.php index 61126c775834..1fae8564d575 100644 --- a/src/Illuminate/Queue/CallQueuedHandler.php +++ b/src/Illuminate/Queue/CallQueuedHandler.php @@ -29,6 +29,13 @@ class CallQueuedHandler */ protected $container; + /** + * Indicates if the unique job lock has been released. + * + * @var bool + */ + protected $uniqueLockReleased = false; + /** * Create a new handler instance. * @@ -171,7 +178,7 @@ protected function ensureSuccessfulBatchJobIsRecorded($command) */ protected function ensureUniqueJobLockIsReleased($command) { - if (! $command instanceof ShouldBeUnique) { + if (! ($command instanceof ShouldBeUnique) || $this->uniqueLockReleased) { return; } @@ -186,6 +193,8 @@ protected function ensureUniqueJobLockIsReleased($command) $cache->lock( 'laravel_unique_job:'.get_class($command).$uniqueId )->forceRelease(); + + $this->uniqueLockReleased = true; } /**