Part 10 of 16 · Day 2 · idempotence
Unknown outcome: did it go through or not?
Problem and experience
The payout timed out. Did it go through or not?
The call to the payment system ends in a timeout. The money may or may not have gone out. Sending the payout again is a risk.
- Disbursement
- Payment system
- Timeoutno response
- Sent?yes / no / unknown
Blind retry
- Second payout
- The client received the money twice
From practiceWhen did you have to decide whether to retry an operation without knowing the result?
TakeawayAn unknown outcome is neither failure nor success. It is a third state.
What it means
An unknown outcome arises when the confirmation does not arrive, but the operation may have gone through. A lost ack is a typical example.
Why we use it
A blind retry creates a duplicate; not retrying creates a loss.
When it makes sense
For all operations with a side effect in an external system: payments, payouts, e-mails.
Where it breaks
- Treating a timeout as a failure.
- Retrying without an operation key.
- Not deciding who finds out the actual state.
Imagine
Disbursement sends the payout and the response is lost. The system tries again. The client receives the money twice.
The worst thing about an unknown outcome is not failure, but not knowing what happened.
Principle, application and exercise
Business idempotency and status checks
Every disbursement has a business key. A retry returns the original result, and an unclear state is verified by a query.
Disbursement key
- Requestkey = applicationId
- Recipienthave this key already?
- yes: original result
- no: execute
Unknown outcome
- Timeout
- Status queryGET /disbursements/{key}
- Decisionretry / close
Trade-offThe key must have business meaning and the retention period must cover the longest retry. Status checking is another process that has to be monitored.
TakeawayIdempotency is a property of the receiver, not the sender.
What it means
Business idempotency uses a key from the business domain (application number, disbursement number). Technical deduplication by eventId alone is not enough when the retry originates elsewhere.
Why we use it
The receiver is the only place where a duplicate can be reliably stopped.
When it makes sense
For every receiver with a side effect.
Where it breaks
- Duplicate check outside the transaction with the effect.
- Deduplication by eventId only.
- Unbounded growth of records.
Imagine
The account-opened event arrives twice. With a key, we get one account and the original account number.
It is not about forbidding duplicates, but making them harmless.
Training for your team
Would you like this training for your team?
We run the training on your code and your domain problem. We agree the scope, dates and format with you.
Discuss the training →