Exactly-once delivery means delivering each sent message one time. Without failures, this is straightforward; with failures, delays, or uncertainty about whether a side effect occurred, the guarantee is impossible. section 4.0 section 4.7
A sender that stops after receiving no response attempts at-most-once delivery: the message may arrive zero or one time. A sender that retries attempts at-least-once delivery, but messages may be duplicated or continuously lost. The sender cannot distinguish an absent response from an unperformed or delayed side effect. section 4.3
Exactly-once processing semantics offer the practical alternative: the final system state behaves as though the message was processed once, even when retries cause multiple processing attempts. This is equivalence, not literal equality; duplicated logs, for example, may be considered inconsequential. The application must define what counts as equivalent. section 4.4
Reliable semantics require an end-to-end design:
- Keep submitting requests until receiving a response, rather than abandoning a possibly unprocessed message. section 4.4
- Ensure the receiver does not skip processing, while making repeated processing idempotent or otherwise equivalent. section 4.4 section 4.7
- Treat crashes, delays, reordering, duplication, and loss as interacting concerns; no universal idempotence solution exists. section 4.7
Charging a credit card
During airline-ticket booking, a missing response may lead an app to retry a card charge. An idempotency key, such as a reservation UUID, can make repeated requests return the same saved result. Without such protection, repeated app crashes and retries may produce several pending charges. section 4.6
requests := { } #1
on message request do #2
if request in received then #3
return
end
received := received + request #4
#5
process(request) // e.g. transfer balance
return
end
This receiver-side pattern records a request before processing it, exposing the crash window in which recording succeeds but processing does not. section 4.3
requests := { }
on message request do
if request in received then
return response
end
process(request) // e.g. transfer balance
#1
received = received + request #2
return response
end
This receiver-side pattern records a request after processing it, exposing the opposite crash window in which processing succeeds but recording does not. section 4.3





