Mannvit retry-safety record
Make the repeat decision visible before a retry exists.
This is a fictional onboarding example and Mannvit design guidance, not a tested client system or vendor contract. The receiving database can store both the onboarding record and the operation result in one transaction. An external email or payment needs its own protection.
Stable operation identity
Completed example: create an onboarding record: Create onboarding for account A-204, agreement version 3; every retry retains that operation key.
Copyable retry-safety record: <name the business action and its key scope>
Protected side effect
Completed example: create an onboarding record: One onboarding record may exist for account A-204 and agreement version 3.
Copyable retry-safety record: <state what must not happen twice>
Repeat behaviour
Completed example: create an onboarding record: A completed event returns the stored result; it does not create another record.
Copyable retry-safety record: <state what a duplicate returns or skips>
Concurrent delivery
Completed example: create an onboarding record: A unique operation key and the onboarding write commit together; a second worker cannot create another result.
Copyable retry-safety record: <state where the claim and effect are enforced>
Unsafe input
Completed example: create an onboarding record: Missing scope is held for review; an intentional field clear is a separate authorised update.
Copyable retry-safety record: <distinguish missing, explicit null and stale input>
Retention
Completed example: create an onboarding record: Old replays require a destination check after the stored operation record expires.
Copyable retry-safety record: <state retention and behaviour after expiry>
Completion proof
Completed example: create an onboarding record: The receiver is read back by account and agreement version and linked to the operation key.
Copyable retry-safety record: <state the destination evidence that proves completion>
Recovery owner
Completed example: create an onboarding record: Revenue operations reviews held records and reconciliation differences.
Copyable retry-safety record: <name the role that owns a non-automatic repair>
Why it matters in practice
Retries are not the exception. They are how reliable integrations work.
A timeout does not tell the sender whether a write happened. Retrying a transient fault can help; repeating a permanent validation failure will not. Microsoft’s Retry pattern describes this distinction and the risk of repeating a write after its response was lost.
Why it runs twice
A delivery can repeat
Amazon SQS standard queues can deliver a message again; AWS explicitly advises idempotent processing. Other queues and webhook providers have their own acknowledgement and retry rules. Design for the actual service contract rather than assuming every provider promises the same thing.
What breaks without it
The retry becomes a second failure
In the fictional example, worker A creates onboarding record O-501 but its response is lost. Worker B receives the same operation. If both workers only check whether the key exists before writing, they can both pass that check. The duplicate check and the protected database write must succeed or fail together.
How to make it safe
Give the operation a stable identity
Choose a key for one intended action, not every action on an account. Save it before sending and reuse it on retries. A create-onboarding key must not also stand for a later cancellation. For a local write, a unique constraint and transaction can protect the result; for a remote call, check the destination’s idempotency contract.
The retry boundary
A retry is a second attempt at the same operation, not permission to make a second business decision.
Worker A commits O-501 and the completed operation record in the same database transaction. When the response is lost, worker B finds the committed result and returns O-501. If A rolls back before committing, B can create the record. If the work is still in progress, B follows a defined wait or conflict path. This example does not make a database transaction cover an unrelated external service.
Limits of the protection
A key does not protect every effect.
Trace the protected write through to its destination. A key written to one database cannot, by itself, settle whether a separate service sent an email or took a payment.
Define update intent. An absent field, an explicit clear and an old value are different inputs. Reject or hold invalid updates under the data contract; do not silently discard every blank, because some clears are legitimate.
Separate delivery history from business effects. Recording each delivery attempt can be useful. Creating a second business ledger entry is different: protect that entry with its own operation identity and an enforced uniqueness rule.
Handle uncertain external results. If the destination may have completed the write before a timeout, inspect its stored result or use its supported retry contract. A local completed flag written afterwards cannot close that crash window.
The reliability rule
At-least-once delivery can run an event more than once; design the side effect accordingly.
Provider rules matter. Stripe API v1 stores the first executed response for a key, including a 500 response, and compares repeat parameters. Keys can be pruned after at least 24 hours; a pruned key does not protect a later replay. API v2 has different retry behaviour. Check the API version rather than treating a key as permanent exactly-once delivery.
