A Series of Unfortunate Jobs
You can't tell me Laravel queues haven't bitten you at least once. It's great that we get them out of the box, but man, they can be confusing as hell sometimes. Over the years, I've screwed up, more than once, and collected quite a few lessons along the way. Claude is currently Pondering.. , I'm waiting, and bored, so shall we? Gotcha #1 I want you to look at the code below and tell me if you can spot anything wrong: <?php namespace App\Jobs ; use DateTime ; use Illuminate\Foundation\Queue\Queueable ; use Illuminate\Contracts\Queue\ShouldQueue ; class SendAbandonedCartReminder implements ShouldQueue { use Queueable ; public function retryUntil (): DateTime { return now () -> addMinutes ( 10 ); } public function handle (): void { // business logic } } SendAbandonedCartReminder :: dispatch ( $cart ) -> delay ( now () -> addDay ()); Nothing sus, right? The customer left their cart, so we nudge them a day later. And if the mail provider is having a bad day, we keep retrying for 10 minutes, then give up. Except that reminder will never be sent. Not once. If you spotted it, you probably learned this one during a fun debugging session 😅. If not, the misconception here is assuming that retryUntil() starts counting from the moment the job starts processing. Well, kind sir, that's where you're wrong. retryUntil() is evaluated when the job is pushed onto the queue, and the expiration timestamp is baked into the job payload. By the time the job becomes available to the workers, a day later, that timestamp is long gone. The worker fails it with the one and only MaxAttemptsExceededException , and handle() never runs. Gotcha #2 Same drill, take a look at the code below: <?php namespace App\Jobs ; use Illuminate\Foundation\Queue\Queueable ; use Illuminate\Contracts\Queue\ShouldQueue ; class SyncOrderToCrm implements ShouldQueue { use Queueable ; public function handle (): void { // business logic } } There is no $tries in sight, sooo, what happens if the job fails? Good question, t