At-least-once means three distinct things can happen to your consumer, and all three appear in this lesson's delivered slice:
- the same event id arrives twice (id 2),
- events arrive out of order (id 3 before id 2),
- the whole stream is redelivered after a restart (pass 2).
Idempotency is a property of the processor, not of the transport. Keep the set of applied event ids in the same store as the data, check it before the effect, and record it as part of the same write.
fn apply_idempotent(&mut self, e: Event) {
if self.seen.contains(&e.id) {
return;
}
self.seen.push(e.id);
self.credit(e.account, e.amount);
}
The naive processor climbs 305 → 610 across two passes. The idempotent one sits at 265 — the exactly-once total — both times.
