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)

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.