2 min read
The pattern
Use a unique constraint on the event id and let the database reject the duplicate for you, rather than a read-then-write that races under concurrent retries.
async function handle(evt) {
try {
await db.insert('processed_events', { id: evt.id }); // unique PK
} catch (e) {
if (isDuplicateKey(e)) return; // already handled — no-op
throw e;
}
await applyEffect(evt); // your business logic
}
Why not read-then-write
Two concurrent retries can both read "not seen" before either writes, and both process the event. The unique-insert pattern makes the database the arbiter, so exactly one wins.
Retention
You only need to remember ids long enough to cover the 72-hour retry window. Prune older rows to keep the table small.
Frequently asked questions
What key should I dedupe on?
The event envelope id (evt_…). It is unique per event and identical across redeliveries of that event.
Does signature verification remove the need for this?
No. Verification proves authenticity, not uniqueness. An authentic event can still be delivered twice. See signature verification.
Funding for UK limited companies
Credicorp lends to your company, not to you personally — short-term working capital with no personal guarantee. See what your business could access.