Fault Tolerance and Exactly-Once — Not Losing or Double-Counting

A stream-processing job runs forever, which means it will experience failures — nodes crash, networks partition, processes restart. The question isn't whether failure happens but what happens to your results when it does: do you lose events, double-count them, or get exactly the right answer anyway? Fault tolerance, and the famous "exactly-once" guarantee, are what separate a toy stream processor from one you can trust with money. This post is about surviving failure correctly.

Post 5 established that stateful streaming’s hardest problem is surviving failure, because state is the accumulated result of processing countless past events. This post is how modern stream processors solve it — checkpointing and exactly-once semantics — and what those guarantees really mean. It’s the concept that makes stateful streaming trustworthy.

The delivery guarantees: at-most, at-least, exactly-once

When a stream processor fails and recovers, each event ends up having been processed in one of three ways, and these define the fundamental guarantees:

The crucial subtlety: “exactly-once” doesn’t mean each event is physically processed exactly once (failures force replays, so physical reprocessing happens) — it means each event’s effect on the state and results is exactly-once. It’s exactly-once semantics: the outcome is correct as if there were no duplicates or losses, achieved despite physical replays. Understanding that distinction (effect, not physical execution) is key to understanding how it’s possible at all.

Why this matters: for many financial and correctness-critical streams (the kind the fintech and AI-financial-engineering work cares about), at-least-once’s duplicates would mean double-counting transactions — unacceptable — so exactly-once is the required guarantee, and it’s a major reason modern stream processors exist.

How exactly-once is achieved: checkpointing

The mechanism behind exactly-once (and fault tolerance generally) is checkpointing: periodically taking a consistent snapshot of the entire job’s state (post 5) and the stream positions, durably, so that on failure the job can restore that snapshot and resume as if the failure hadn’t happened.

The idea, at a high level:

This gives at-least-once on its own (replayed events after the checkpoint get reprocessed). To reach exactly-once, two more things are needed: - State updates are tied to checkpoints so that on restore, the state reflects exactly the checkpointed position — no double-applied updates to state. - Outputs/sinks must cooperate. The genuinely hard part: the external effects (writing to a database, sending to another system) must also be exactly-once, which requires transactional or idempotent sinks — the sink either participates in a transaction committed with the checkpoint (two-phase commit), or is idempotent so replayed writes don’t duplicate. Exactly-once end-to-end requires the whole pipeline (source → processing → sink) to cooperate, and the sink is usually the limiting factor.

So exactly-once is: consistent checkpointing of state+positions, replayable sources, and transactional/idempotent sinks — together producing correct results despite failures and physical replays.

What it costs, and choosing the right guarantee

Exactly-once is powerful but not free, and the engineering judgment is choosing the guarantee that fits:

The through-line: a forever-running stream job will fail, so fault tolerance is non-negotiable, and the guarantee you provide — at-most-once (may lose), at-least-once (may duplicate), exactly-once (correct as if neither) — determines whether your results survive failure correctly. Exactly-once semantics (correct effects despite physical replays) is achieved by consistent checkpointing of distributed state and stream positions to durable storage, replayable sources, and transactional/idempotent sinks — restoring and replaying from the last checkpoint so the job resumes as if nothing failed. It costs performance, so choose the guarantee by the correctness need, using idempotency as the pragmatic route to correctness where full exactly-once isn’t warranted. This is the machinery that makes stateful streaming trustworthy enough to count money on, and it’s a defining capability of serious stream processors.

The throughline: a forever-running stream job will fail, so fault tolerance is non-negotiable and the delivery guarantee decides whether results survive correctly: at-most-once (0-or-1 processing — may lose events; fast/simple, weakest), at-least-once (1-or-more — no loss but duplicates; fine if processing is idempotent), or exactly-once (each event affects the result exactly one time despite failures — the gold standard for money/billing/exact counts). Crucially, exactly-once means exactly-once semantics (correct effect on state/results despite physical replays), not physical single execution. It’s achieved by checkpointing — periodic consistent snapshots of all distributed state (post 5) plus stream positions, written durably — with recovery by restore-and-replay from the last checkpoint over replayable sources (Kafka offsets), plus transactional/idempotent sinks for end-to-end exactly-once (the sink is usually the limiting factor). Stronger guarantees cost throughput/latency, so choose by correctness need, using idempotency as the cheap route to correctness.

Key takeaways

Further reading

Sources & References