SkillByAIOpen interactive version →

Lesson 15 / 25

Idempotency

Retries without double charges.

Idempotency keys

Networks time out after the server has done the work, so clients retry, and queues redeliver. An operation is idempotent if repeating it has the same effect as doing it once. For non-idempotent actions such as payments, the client sends an idempotency key; the server stores the result per key and returns the stored result on repeats instead of acting again. Store keys with the result atomically and expire them after a sensible window.

A retried payment charged only once, run

I ran this with Python 3.12.3 using only the standard library; inputs are fixed or seeded, so the output is reproducible. The retry with the same key returns the stored result marked as replayed, and only two real charges happen for three calls.

# Idempotency keys make retried payment requests safe
import uuid

class PaymentAPI:
    def __init__(self):
        self.results, self.charged = {}, []
    def charge(self, key, amount):
        if key in self.results:                 # replay: return the stored result
            return self.results[key] | {"replayed": True}
        self.charged.append(amount)              # the real side effect happens once
        result = {"status": "succeeded", "amount": amount, "replayed": False}
        self.results[key] = result
        return result

api = PaymentAPI()
key = str(uuid.UUID(int=42))                    # fixed key for a reproducible demo
print(api.charge(key, 499))
print(api.charge(key, 499))                     # client timed out and retried
print(api.charge(str(uuid.UUID(int=43)), 499))  # a genuinely new payment
print("total charged:", sum(api.charged))

Output:

{'status': 'succeeded', 'amount': 499, 'replayed': False}
{'status': 'succeeded', 'amount': 499, 'replayed': True}
{'status': 'succeeded', 'amount': 499, 'replayed': False}
total charged: 998

Design consumers to be idempotent

Upserts, deduplication tables and conditional updates make at-least-once delivery safe.

Quick check: What should a server do when it receives a repeated idempotency key?

  • Return the stored result without repeating the side effect
  • Charge again
  • Return an error and lose the result
  • Ignore the key and process normally
Answer

Return the stored result without repeating the side effect — Same effect as once.