Confluent's docs say Kafka supports exactly-once semantics. Your consumer writes to a Delta table. What is actually guaranteed end to end, and what do you still have to build?
The setup — predict the table before reading on
fct_payment starts empty. stg_payment holds one delivery at a time.
The load — run once per delivery
INSERT INTO fct_payment SELECT event_id, customer, amount FROM stg_payment s WHERE NOT EXISTS (SELECT 1 FROM fct_payment f WHERE f.event_id = s.event_id);
The deliveries, in order
1the broker delivers the batch
At-least-once, which is what every broker worth using actually offers.
stg_payment — delivery 1 event_id customer amount p1 cust-a 100 p2 cust-b 250 2and delivers it again after the consumer failed to ack
Nothing about the transport changed. The redelivery is not a bug.
stg_payment — delivery 2 event_id customer amount p1 cust-a 100 p2 cust-b 250
Why they ask this
It is the fastest way to find out whether a candidate repeats marketing language or understands the mechanism. The phrase is on the front page of every streaming product and it means something much narrower than it sounds.
Say this
They can promise exactly-once *processing within their own system* — no more. Across a boundary they do not control, delivery is at-least-once, and the once-ness has to come from a write that absorbs a repeat.
The reasoning
Start from why at-least-once is unavoidable. A sender transmits, the receiver processes, the acknowledgement is lost on the way back. The sender now cannot distinguish between a message that never arrived and one that arrived and was processed, so it has two options: send again, or do not. Sending again gives at-least-once. Not sending gives at-most-once. There is no third option, and this is a property of unreliable networks, not of any particular product.
What a system like Kafka or Flink can genuinely offer is exactly-once *within a closed world*: offsets and output are committed in one transaction, so a restart cannot double-apply anything the system itself controls. That is real and it is valuable. It stops applying the moment the output leaves — a warehouse, an API, an email — because the platform can no longer make the write and the offset atomic.
So the once-ness you can actually rely on end to end is built at the write. Give every unit of work an identity the sink can recognise, and let the sink discard a repeat. The run below is the whole idea in miniature: the broker delivers the same batch twice, which is normal and not a fault, and the write keyed on event_id leaves the table identical. The industry name for this is effectively-once, and it is what every production system you have used is actually doing.
The run — 2 deliveries, in order replay changed nothing · matches a full refresh, verified
The same batch delivered twice. The sink recognises both payments and takes neither again.
1after the broker delivers the batch
fct_payment event_id customer amount p1 cust-a 100 p2 cust-b 250 2after and delivers it again after the consumer failed to ack
fct_payment event_id customer amount p1 cust-a 100 p2 cust-b 250
And the same table, recomputed from scratch over everything ever delivered
SELECT DISTINCT event_id, customer, amount FROM delivered ORDER BY event_id
| event_id | customer | amount |
|---|---|---|
| p1 | cust-a | 100 |
| p2 | cust-b | 250 |
The answer most people give
"We use Kafka with exactly-once semantics enabled, so we are covered." That setting covers Kafka-to-Kafka processing. The moment your consumer writes to a warehouse, that guarantee has ended and the burden is entirely on your write.
They’ll ask next
Your sink is a third-party REST API with no idempotency key. Now what?
