Keep similar-ai from tripping the global circuit on a lone URL 502, clamp Qdrant search to 100, and add Server-Timing plus slow-request logging. Studio shared props, Academy S3 exists caching, heat chunking, and Redis/scheduler hygiene stay in this rollout.
2.5 KiB
M2 — Mail Queue Consumption
Root cause
Application mail jobs are pushed to Redis queue mail. Production Horizon supervisors only consume:
search, default
broadcasts, notifications
There is no queue:work process for mail. The three items have attempts=0 — they were never reserved.
Isolation is intentional in code (onQueue('mail')) and in docs/QUEUE.md. Horizon simply never subscribed.
The three queued items (no PII)
Inspected with LRANGE/LINDEX via Laravel Redis; only class metadata:
| # | displayName | maxTries | attempts |
|---|---|---|---|
| 0 | App\Jobs\SendVerificationEmailJob |
5 | 0 |
| 1 | App\Mail\EmailChangedSecurityAlertMail |
3 | 0 |
| 2 | App\Mail\EmailChangeVerificationCodeMail |
3 | 0 |
pushedAt unix times: 1777665006, 1777697989, 1784800925 (older registration/email-change mail sitting until a worker exists).
Local vs production
Same gap on both: Horizon defaults omitted mail. Production config('horizon.defaults.supervisor-*.queue') confirmed ["search","default"] and ["broadcasts","notifications"].
Legacy deploy/supervisor/skinbase-queue.conf does include mail, but production runs Horizon, not that unit.
Fix
Added Horizon supervisor-mail:
queue: mail
connection: redis
tries: 5 (matches SendVerificationEmailJob; rate-limit uses release())
timeout: 90
maxProcesses: 1 local / 2 production
Kept isolation so SMTP does not block rec/search workers and so --tries=1 on default workers cannot fail mail jobs on release().
Files changed
config/horizon.php
tests/Unit/HorizonMailQueueTest.php
docs/QUEUE.md
docs/optimization-m2-mail-queue.md
Deployment
- Deploy release with
config/horizon.php. php artisan config:cache/optimizeas usual.- Restart Horizon (Supervisor
skinbase-horizon/queue:restart). - Confirm
php artisan horizon:statusand a worker with--queue=mail.
No .env change required. No Redis delete.
Should the 3 messages be processed after deploy?
Yes, automatically once supervisor-mail starts — do not delete the list.
Risks:
- Recipients may receive late verification/security emails.
- Tokens/codes may already be expired; user sees a failed verify, not a security hole from us sending mail.
SendVerificationEmailJobmay still sendRegistrationVerificationMail(alsomailqueue) — second job is fine once workers run.- Do not manually
queue:retryorlpopbefore Horizon is up.
Tests
php artisan test --filter=HorizonMailQueue