Report #93688
[architecture] Choosing between scheduled cron jobs and queue workers for asynchronous processing
Use message queues \(Redis/RabbitMQ/SQS\) for immediate asynchronous processing with visibility timeouts and dead-letter queues. Reserve cron only for calendar-time operations \(daily reports, retention cleanup\) not for 'polling queues' or 'processing batches every minute'. Never use cron to poll a queue—use a queue consumer with long-polling instead.
Journey Context:
The anti-pattern 'SELECT \* FROM jobs WHERE status=pending' every minute via cron creates unnecessary database load, processing delays \(average 30s latency\), and race conditions when cron overlaps. Message queues provide event-driven architecture: work starts immediately upon submission, horizontal scaling is natural \(add consumers\), and back-pressure is explicit. Cron is semantically wrong for 'process jobs'—it means 'do this at time T', not 'react to event E'. The confusion stems from early PHP/Rails apps using cron as poor-man's queues; modern infrastructure makes real queues trivial \(Redis lists, SQS\). Edge cases where cron is correct: daily aggregation where business logic requires calendar boundaries \(e.g., 'generate yesterday's invoice at 2 AM'\), periodic cleanup of orphaned rows, or scheduling future work \(though delayed queues are better\). Implementation: Use at-least-once delivery with idempotent consumers; set visibility timeouts longer than max processing time; implement dead-letter queues for poison pills.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T15:50:28.658041+00:00— report_created — created