Opt-out honored during queue backlog
Why it matters
GDPR Article 7(3) and CAN-SPAM §7704(a)(4) impose time-bounded opt-out obligations with no exception for queue backlog. When a large campaign send floods the job queue at the same time as incoming opt-out requests, a FIFO queue without priority differentiation can delay opt-out processing by hours — pushing real contacts into violation territory. Separately, if the campaign recipient list is built at scheduling time (hours before send), contacts who opted out after list construction will be in the send batch regardless of the queue's behavior. Both failure modes cause sends to contacts who have legally withdrawn consent.
Severity rationale
Medium because queue backlog delays opt-out processing into regulatory violation territory under high send volumes, and stale recipient lists create a deterministic window where opted-out contacts are unreachable by the suppression system.
Remediation
Set explicit job priority in BullMQ (lower number = higher priority) and rebuild the recipient list immediately before send, not at schedule time:
// src/workers/queue.ts — opt-out jobs at priority 1, campaign sends at priority 10
await queue.add('process-optout', data, { priority: 1 })
await queue.add('send-campaign-email', data, { priority: 10 })
// src/lib/campaigns/recipients.ts — called at send time, not schedule time
async function getCampaignRecipients(campaignId: string): Promise<string[]> {
const baseList = await db.campaignAudience.findMany({ where: { campaignId } })
const ids = baseList.map(r => r.contactId)
const suppressed = await db.contact.findMany({
where: { id: { in: ids }, optOutStatus: { not: null } }
})
const suppressedSet = new Set(suppressed.map(c => c.id))
return ids.filter(id => !suppressedSet.has(id))
}
Never cache the recipient list for more than 5 minutes before the actual send batch starts.
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 Compliance & Consent Engine Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- gdpr · Art. 7(3) — Processing based on consent must stop when withdrawn
- external · CAN-SPAM-§7704(a)(4) — CAN-SPAM Act — 10 business day processing window regardless of queue state
- iso-25010:2011 · reliability.fault-tolerance — Fault tolerance — system must honour opt-outs under load
Taxons
History
- 2026-04-18·v1.0.0·Initial import from compliance-consent-engine·automated