Short answer: for a small app retrying failed nightly payment-reconciliation jobs, use a queue with a dead-letter queue (DLQ) and redrive by default; manual database polling is reasonable only while concurrency is tiny and the database row itself is the audit record you need.
The evaluation constraint matters more than the sticker price. This job runs overnight, so shaving milliseconds from dispatch does little for customers, while duplicate payment-side effects and hours spent maintaining retry state are expensive. I would optimize for low operating effort and a transport boundary that can be replaced later. Don't let the scheduler own the long-running work.
Infrai fits one specific version of that boundary early in the design: cron and queue operations are available through plain REST, so the app doesn't inherit an SDK dependency.…