Stream Processing ArchitecturesExactly-Once Processing SemanticsMedium⏱️ ~3 min

How Exactly-Once Processing Works

The Fundamental Approach: Exactly-once processing combines three core techniques to handle duplicate message deliveries correctly. First is idempotency, where replaying an operation multiple times produces the same final state as executing it once. Second is atomicity, ensuring that reading input, updating state, and writing output happen as a single unit. Third is durable checkpoints, saving your progress so recovery knows exactly where to resume. These three pieces work together in a specific pattern that repeats continuously during normal operation.
1
Read and Process: Consume messages from the input stream and apply your business logic, updating internal state like counters or aggregations.
2
Checkpoint State: Periodically (every 5 to 10 seconds typically), snapshot your operator state and the input offsets you have processed to durable storage like S3 or distributed filesystem.
3
Write Outputs: Publish results to output topics or databases using transactional or idempotent mechanisms that coordinate with the checkpoint.
4
Commit Atomically: Mark the checkpoint as complete only when both state and outputs are durably saved, making this recovery point official.
5
On Failure Recovery: Restore the last committed checkpoint state and resume reading from the saved input offsets, reprocessing only what happened after that checkpoint.
Why This Works: The magic is in the atomicity of step 4. When a failure happens, either the entire checkpoint committed (state and outputs are both saved and visible) or it did not (neither are visible). There is no middle ground where outputs went out but state was lost, or vice versa. On recovery, you replay from the last successful checkpoint. Any messages processed after that checkpoint but before the failure get reprocessed. But because outputs from that partial work were never committed, the external world never saw them. Reprocessing produces the same outputs again, which now get committed successfully. The final result is identical to processing each message exactly once. Real System Example: Apache Flink implements this pattern at massive scale. A Flink job processing 500,000 events per second might checkpoint every 5 seconds. Each checkpoint writes operator state (perhaps 2 GB of aggregated data) to S3 and coordinates with output sinks using two phase commit semantics. If a task manager crashes at second 7, Flink restores the second 5 checkpoint and replays those 2 seconds of input, roughly 1 million events. Because the partial outputs from seconds 5 to 7 were never committed, downstream systems never saw them. The replay produces identical outputs that now commit successfully.
✓ In Practice: Kafka Streams uses a similar approach but leverages Kafka transactions directly. It writes state changes to internal changelog topics and output to result topics, all within a single transaction that also commits consumer offsets.
💡 Key Takeaways
Checkpoints snapshot both operator state and input offsets every 5 to 10 seconds, creating recovery points that cover all processing work
Outputs are written transactionally or idempotently, coordinating with checkpoint commits to ensure atomicity across state and results
Recovery restores the last successful checkpoint and replays from saved offsets, reprocessing messages that occurred after that checkpoint
The pattern ensures either both state and outputs commit together or neither commits, preventing partial failures that would break exactly-once guarantees
Systems like Flink handle 500,000 events per second with this pattern, checkpointing gigabytes of state to durable storage like S3 every few seconds
📌 Examples
1A Flink job checkpoints at second 0, 5, 10. It crashes at second 7. Recovery loads the second 5 checkpoint and replays seconds 5 to 7, reprocessing roughly 1 million events at 500K events/sec rate.
2Kafka Streams processing payments writes state changes to a changelog topic and results to an output topic, both within a single Kafka transaction that includes consumer offset commits.
3A stateful aggregation maintains running counts per user. The checkpoint at offset 1000 saves counts and offset together. On restart, it loads those counts and resumes from offset 1000, ensuring no duplicates or missed events.
← Back to Exactly-Once Processing Semantics Overview
How Exactly-Once Processing Works | Exactly-Once Processing Semantics - System Overflow