Idempotency & Retry-Safe Systems · Why Retries Happen and What Idempotency Means
Designing Idempotent Writes
Patterns: absolute updates, conditional writes, upserts and state machines.
Design writes so repeats are harmless. Use state machines where transitions are guarded (paid → shipped only from paid); a repeated transition becomes a no-op or a clear 409. Use conditional writes (WHERE version = ?, If-Match) and upserts keyed by stable identifiers. Record processed operation IDs alongside side effects in the same transaction. Return the same result for a repeated request when possible so clients can proceed.
async def mark_shipped(db, order_id: str, shipment_id: str) -> Order:
# Idempotent transition: repeating with the same shipment_id is a no-op
row = await db.fetchrow(
'''UPDATE orders SET status = 'shipped', shipment_id = $2
WHERE id = $1 AND (status = 'paid' OR (status = 'shipped' AND shipment_id = $2))
RETURNING *''', order_id, shipment_id)
if row is None:
raise Conflict(f"Order {order_id} cannot be shipped from its current state")
return Order(**row)
export async function markShipped(orderId: string, shipmentId: string) {
const { rows } = await pool.query(
`UPDATE orders SET status = 'shipped', shipment_id = $2
WHERE id = $1 AND (status = 'paid' OR (status = 'shipped' AND shipment_id = $2))
RETURNING *`, [orderId, shipmentId]);
if (rows.length === 0) throw new ConflictError(`Order ${orderId} cannot be shipped`);
return rows[0];
}
@Modifying
@Query("""
update Order o set o.status = 'SHIPPED', o.shipmentId = :shipmentId
where o.id = :id and (o.status = 'PAID' or (o.status = 'SHIPPED' and o.shipmentId = :shipmentId))
""")
int markShipped(String id, String shipmentId); // 0 rows → 409 Conflict
Common pitfalls
- Read-then-write checks without atomic conditions (races).
- Returning errors for harmless repeats, causing client confusion.
- Side effects (emails) triggered on every repeat of a transition.
Key points
- Guarded state transitions
- Atomic conditional writes and upserts
- Repeat → same outcome
Further reading: AWS Builders' Library
Practice questions
Why should repeating 'ship order with shipment S' succeed rather than fail?
Answer
A retry of a successful request should produce the same outcome so the client can continue; failing it would make clients think the original attempt failed.
How do you avoid sending a 'shipped' email twice?
Answer
Trigger it only when the state actually changes (rows affected = 1 and previous state was 'paid'), or dedupe the email by order id + event type.
Open in the app to mark lessons complete, take notes and track your progress.