From cf92b1cb29f3fb5c2974f46815991b28e11c30e7 Mon Sep 17 00:00:00 2001 From: Joel-Jensen <60270137+Joel-Jensen@users.noreply.github.com> Date: Wed, 11 Nov 2026 12:49:18 +0100 Subject: [PATCH] Cap timer delays on Laravel Cloud's managed queues Laravel Cloud's managed queues use the `cloud` driver, which is backed by SQS and refuses a delay over 900 seconds. The timer only capped its wake-up for the `sqs` driver, so on Cloud any timer longer than 15 minutes threw an SqsException from SendMessage. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/Traits/Timers.php | 3 +- tests/Unit/Traits/TimersTest.php | 52 ++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/Traits/Timers.php b/src/Traits/Timers.php index 35c133bd9..ad63e8b50 100644 --- a/src/Traits/Timers.php +++ b/src/Traits/Timers.php @@ -84,7 +84,8 @@ public static function timer(int|string|CarbonInterval $seconds): PromiseInterfa $connection = self::connection() ?? config('queue.default'); $driver = config('queue.connections.' . $connection . '.driver'); - if ($driver === 'sqs') { + // Laravel Cloud's managed queues use the `cloud` driver, which is backed by SQS. + if (in_array($driver, ['sqs', 'cloud'], true)) { $maxDelay = self::$context->now->copy()->addSeconds(900); if ($timer->stop_at->greaterThan($maxDelay)) { $delay = $maxDelay; diff --git a/tests/Unit/Traits/TimersTest.php b/tests/Unit/Traits/TimersTest.php index 947914a6e..b4c2df74b 100644 --- a/tests/Unit/Traits/TimersTest.php +++ b/tests/Unit/Traits/TimersTest.php @@ -361,4 +361,56 @@ public function testTimerCapsDelayForSqsDriver(): void return true; }); } + + public function testTimerCapsDelayForLaravelCloudDriver(): void + { + Bus::fake(); + + config([ + 'queue.default' => 'cloud', + ]); + config([ + 'queue.connections.cloud' => [ + 'driver' => 'cloud', + 'queue' => 'default', + ], + ]); + + $workflow = WorkflowStub::load(WorkflowStub::make(TestWorkflow::class)->id()); + $storedWorkflow = StoredWorkflow::findOrFail($workflow->id()); + $storedWorkflow->class = TestChildWorkflow::class; + $storedWorkflow->update([ + 'arguments' => Serializer::serialize([]), + 'status' => WorkflowPendingStatus::$name, + ]); + + $now = now(); + WorkflowStub::setContext([ + 'storedWorkflow' => $storedWorkflow, + 'index' => 0, + 'now' => $now, + 'replaying' => false, + ]); + + $storedWorkflow->timers() + ->create([ + 'index' => 0, + 'stop_at' => $now->copy() + ->addHour(), + ]); + + WorkflowStub::timer(3600) + ->then(static function ($value) use (&$result) { + $result = $value; + }); + + $this->assertNull($result); + + Bus::assertDispatched(Timer::class, function ($job) use ($now) { + $delaySeconds = $job->delay->diffInSeconds($now); + $this->assertLessThanOrEqual(900, $delaySeconds); + $this->assertGreaterThanOrEqual(899, $delaySeconds); + return true; + }); + } }