At-least-once delivery with deduplication guard
Why it matters
At-least-once delivery guarantees that a job is processed at least once — it does not guarantee it is processed exactly once. If a worker sends a password-reset email, then crashes before acknowledging the job, BullMQ re-queues it and the next worker pickup sends an identical email. For transactional messages, duplicates erode user trust immediately. CWE-362 covers race conditions on shared resources; duplicate sends are precisely that class of failure. ISO-25010:2011 reliability.fault-tolerance requires the system to behave correctly despite component failures.
Severity rationale
High because worker crashes during network I/O are routine events, and without a dedup guard every such crash produces a duplicate transactional email delivered to real recipients.
Remediation
Add a pre-flight dedup check at the start of the job processor in workers/email.worker.ts, before calling the ESP:
async function processEmailJob(job: Job<EmailJobData>) {
const { campaignId, recipientId } = job.data
const existing = await db.emailSendLog.findFirst({
where: { campaignId, recipientId, status: 'sent' }
})
if (existing) {
logger.info({ campaignId, recipientId }, 'Duplicate job — skipping')
return
}
await db.emailSendLog.create({
data: { campaignId, recipientId, status: 'sending', jobId: job.id }
})
const result = await esp.send(job.data)
await db.emailSendLog.update({
where: { campaignId_recipientId: { campaignId, recipientId } },
data: { status: 'sent', espMessageId: result.messageId, sentAt: new Date() }
})
}
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 Sending Pipeline & Infrastructure Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- cwe · CWE-362 — Race Condition
- iso-25010:2011 · reliability.fault-tolerance — Fault Tolerance
Taxons
History
- 2026-04-18·v1.0.0·Initial import from sending-pipeline-infrastructure·automated