Pre-appointment reminder sent at configurable interval via scheduled job, not polling loop
Why it matters
A setInterval or while(true) polling loop that checks all bookings every N seconds keeps a database connection open and runs a full-table scan on every iteration — even when there are zero due reminders. Under load, this pattern burns CPU, database query budget, and queue worker threads continuously. CWE-770 (uncontrolled resource consumption) and ISO 25010 performance-efficiency.resource-utilization both capture this failure. A cron-based job queue fires only when reminders are actually due, scales horizontally without contention, and is restartable after a crash. Beyond efficiency, a polling loop running in a long-lived process cannot recover gracefully from crashes — in-flight checks are simply lost.
Severity rationale
Critical because a polling loop consumes database and compute resources at all times regardless of reminder volume, and silently loses in-flight checks when the process crashes.
Remediation
Replace any setInterval or setTimeout loop with a BullMQ repeatable cron job in src/jobs/reminders.ts. Store the reminder interval per service in the database so it can be adjusted without a code deploy:
const reminderQueue = new Queue('reminders', { connection: redis })
// Cron fires every 5 minutes — only does work when reminders are due
await reminderQueue.add('check-due-reminders', {}, { repeat: { pattern: '*/5 * * * *' } })
const worker = new Worker('reminders', async (job) => {
if (job.name === 'check-due-reminders') {
const dueBookings = await db.bookings.findMany({
where: { reminder_due_at: { lte: new Date() }, reminder_sent: false, status: 'confirmed' },
})
for (const booking of dueBookings) {
await reminderQueue.add('send-reminder', { bookingId: booking.id })
}
}
}, { connection: redis })
Add an index on (reminder_due_at, reminder_sent, status) to keep the due-bookings query fast as volume grows.
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Booking Notifications & Reminders Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- cwe · CWE-770 — Allocation of Resources Without Limits or Throttling (uncontrolled polling)
- iso-25010:2011 · performance-efficiency.resource-utilization
Taxons
History
- 2026-04-18·v1.0.0·Initial import from booking-notifications-reminders·automated