The defaults are fine for a tutorial. They are not fine for a job that charges a card or sends 40,000 emails.
Almost every Laravel app I take over has queues configured exactly as the framework shipped them. That works until the first busy Monday, when a slow job runs twice or a burst of newsletter emails delays every password reset by twenty minutes.
Timeout shorter than retry_after
If a job's timeout is longer than the connection's retry_after, a slow job is handed to a second worker while the first is still running. For a job that charges a card, that is a double charge. I set the timeout to roughly two thirds of retry_after and make the slow jobs explicit about it.
Separate queues for separate promises
Payments and password resets get their own high queue with a dedicated worker. Newsletters, reports and image processing go on a low queue. A burst of 40,000 emails should never sit in front of a customer waiting for a login link.
Every job should also be safe to run twice, because eventually one will. For payments that means checking the gateway's idempotency key or the payment status before doing anything. For emails it means stamping a sent_at column and checking it first.
Watch the failed jobs table
Failed job storage should be on, and someone should look at it weekly. I add a scheduled command that posts the count of new failures to the team's Slack channel every Monday morning. It costs nothing and catches broken integrations days before a customer emails.
Finally, run workers under Supervisor and call queue:restart on every deploy. Otherwise the workers keep running yesterday's code, and the bug you just fixed is still happening in the background.
Comments