At-Most-Once, At-Least-Once, and Effectively-Once Processing

At-most-once delivery avoids duplicates by risking loss. At-least-once delivery retries until acknowledgment or policy limits, so duplicates are expected. Exactly-once claims are usually scoped to a particular broker transaction or processing boundary; they do not automatically cover arbitrary external side effects.

A common practical design is at-least-once delivery plus an idempotent consumer. Give each event a stable ID and record processed IDs in the same transaction as the business update. On duplicate delivery, the consumer detects the prior completion and avoids applying the effect twice.

For external actions such as sending money or calling a third-party API, use that provider's idempotency support where available and reconcile uncertain outcomes. A local “processed” flag written separately from the business update can itself race or fail, so place deduplication and state changes in one atomic boundary whenever possible.

Idempotent consumer pseudocode
async function handleEvent(event) {
  await db.transaction(async (tx) => {
    const alreadyProcessed = await tx.processedEvents.exists(event.id);
    if (alreadyProcessed) return;

    await applyBusinessChange(tx, event);
    await tx.processedEvents.insert({ eventId: event.id });
  });
  // Acknowledge the message only after the transaction commits.
}
💡 The event marker and business update must commit atomically. If the process crashes before acknowledgment, redelivery sees the marker and does not repeat the change.
Key Takeaways
  • Assume duplicate delivery unless the full boundary proves otherwise.
  • Deduplication and the business mutation should be atomic.
  • Exactly-once guarantees must be scoped precisely.