The outbox from lesson 4.6.2 delivers at least once. Duplicates are therefore no longer an accident but part of the contract: The relay sends again after a crash, Service Bus delivers again after a lock has expired, a second receiver gets the same message while the first is still working. In this lesson you build the other half: receivers for which a message has the same effect, no matter how often it comes. And you learn what to do when the effect doesn't fit into your transaction at all.
Idempotent by nature
Some effects are idempotent by themselves: Executed twice, they change nothing more than executed once.
- Setting an absolute value instead of adding something: “status is won” instead of “number of wins plus one”.
- Insert or replace by business key (upsert): A tender with its publication number is in the table once afterward, no matter how often the import delivers it.
- A state transition with a rule: “Win the deal with quote 1001” does nothing if the deal has already been won with exactly this quote.
Microsoft's Idempotent Consumer pattern advises preferring such effects and using the bookkeeping of processed messages only where there is no other way. Not idempotent by themselves are: adding something, creating a row with a new identifier, sending a mail, calling a foreign service that moves money.
Where an effect is not idempotent by itself, the receiver needs a key by which it recognizes a repetition. Two kinds come into question:
- the identifier of the message (
MessageId) that the sender assigned stably (lesson 4.6.2); - a business key for the one operation, such as “quote 1001 accepted”. It also helps when a sender accidentally sends the same intent in two different messages.
Unsuitable are values that arise anew with every delivery: the broker's sequence number, the time of receipt, the number of delivery
attempts. And the CorrelationId is not a key: It connects several messages that belong together, such as a request and its answers.