Skip to course content
Free generative AI app course

Generative AI Application Development with Python

Unit 09.02: Retrying a call that may already have worked

A timeout is the one failure where you genuinely cannot tell whether it worked.

The retry is unavoidable; the key makes it safe

A ticket creation that times out and is retried.

The code shows one ticket created.

CALLS = []


def create_ticket(subject, key):
    for existing_key, ticket in CALLS:
        if existing_key == key:
            return {"status": "already created", "id": ticket}
    ticket = f"TCK-{len(CALLS) + 1}"
    CALLS.append((key, ticket))
    return {"status": "created", "id": ticket}


key = "req-8841:create-ticket"
print("first call times out -- we do not know whether it succeeded")
print("first: ", create_ticket("charged twice", key))
print("retry: ", create_ticket("charged twice", key))
print(f"\ntickets actually created: {len(CALLS)}")

# A timeout is the failure that is genuinely ambiguous: you were charged, the
# call may have completed, and you have no way to tell. The idempotency key is
# what makes the unavoidable retry safe.

You were charged, the call may have completed upstream, and there is no way to find out from your side without another call. So the retry happens, and the key is what stops it being a second ticket.

This is why the idempotency key from Module 6 is not optional for anything with a side effect. The ambiguous case is not rare.

The mistake this prevents

The mistake is treating a timeout as a failure and retrying without a key, because the request "clearly did not succeed". It very often did - the response was what was lost.

Takeaway

Treat a timeout as ambiguous rather than as a failure. Retry with an idempotency key, because the call may already have done the thing.