diff --git a/packages/queue/src/Exception/RetryAfterException.php b/packages/queue/src/Exception/RetryAfterException.php new file mode 100644 index 000000000..c486a0899 --- /dev/null +++ b/packages/queue/src/Exception/RetryAfterException.php @@ -0,0 +1,13 @@ +defer = new DeferredException($delay, $reason); } - public function retryAfter(int $delay): static + public function retryAfter(int $delay): RetryAfterException { - $this->retryAfter = max(0, $delay); + $this->retryAfter = $delay = max(0, $delay); - return $this; + return new RetryAfterException($delay); } public function resume(): static @@ -370,6 +371,8 @@ protected function invokeMiddleware(mixed $middleware): mixed } return $result; + } catch (RetryAfterException $e) { + $this->retryAfter = $e->delay; } catch (DeferredException $e) { $this->defer = $e; diff --git a/packages/queue/test/Job/JobControllerTest.php b/packages/queue/test/Job/JobControllerTest.php index 0425fb4cd..7a0cc512c 100644 --- a/packages/queue/test/Job/JobControllerTest.php +++ b/packages/queue/test/Job/JobControllerTest.php @@ -11,6 +11,7 @@ use Windwalker\Queue\Attributes\JobMiddlewaresProvider; use Windwalker\Queue\Exception\AbandonedException; use Windwalker\Queue\Exception\DeferredException; +use Windwalker\Queue\Exception\RetryAfterException; use Windwalker\Queue\Job\JobController; use Windwalker\Queue\Middleware\QueueMiddlewareInterface; use Windwalker\Queue\QueueMessage; @@ -169,7 +170,7 @@ public function retryAfterStoresDelayAndResumeClearsIt(): void $controller = new QueueMessage(new TestJob())->makeJobController(); self::assertNull($controller->retryAfter); - self::assertSame($controller, $controller->retryAfter(30)); + self::assertInstanceOf(RetryAfterException::class, $controller->retryAfter(30)); self::assertSame(30, $controller->retryAfter); self::assertFalse($controller->failed); @@ -177,6 +178,26 @@ public function retryAfterStoresDelayAndResumeClearsIt(): void self::assertNull($controller->retryAfter); } + #[Test] + public function retryAfterCanBeThrownToStopJobExecution(): void + { + $job = new class () { + public bool $continued = false; + + public function __invoke(JobController $controller): void + { + throw $controller->retryAfter(30); + + $this->continued = true; + } + }; + $controller = new QueueMessage($job)->makeJobController()->run(); + + self::assertFalse($job->continued); + self::assertSame(30, $controller->retryAfter); + self::assertFalse($controller->failed); + } + #[Test] public function logWritesEachMessageToLogger(): void {