Week 2 · Foundation

Retry without double-charging

0:00 / 3:00

Guess first

The retry worked. Twice.

Your client retried a payment after a timeout. Northwind processed both. What would have stopped the second one?

Your serviceRetryPayments APISeen this key?Key storeabc-123: done
Lesson notesRetry without double-charging: the short version+

Week 2 · Production Python · about 3 minutes

What you will be able to do

Make write retries safe with an idempotency key created once per operation.

The idea

A timeout doesn't tell you whether the server did the work. Retrying a read is safe. Retrying a write can do it twice: two payments, two tickets, two emails.

Send an idempotency key: a unique ID created once per operation and reused on every retry. The server saves the key with the result, and when the same key arrives again it returns the saved result instead of doing the work again.

Create the key before the first attempt, never inside the retry loop.

key = str(uuid.uuid4())  # once per payment, before any attempt

for attempt in range(3):
    try:
        r = client.post("/payments", json=payment,
                        headers={"Idempotency-Key": key})
        r.raise_for_status()
        return r.json()
    except httpx.TimeoutException:
        time.sleep(2 ** attempt)

A retry is only safe when the server can recognise a repeat.

Key takeaways

  • Reads are safe to retry; writes need a key.
  • Create the key once, before the first attempt.
  • The server returns the saved result for a repeated key.

Part of the Forward Deployed Engineer roadmap. New to the role? Start with what a Forward Deployed Engineer does.