Skip to content
About
Main content

Idempotent consumers: the half the outbox exports

Who pays for at-least-once delivery when a transaction spans systems?

The half the outbox exports

The and every step guarantee at-least-once delivery, so the consumer must make a repeat harmless. How that is done (exactly-once is a property of effects, never of delivery; natural idempotence or a dedup key; the retention window a cannot verify) is fault tolerance's subject. For a distributed transaction the point is where the cost lands: the consumer that the outbox bills. A minimal consumer that dedups on the event's ID, inside the same local transaction as its effect:

def handle(event):
    with db.transaction():
        inserted = db.execute(
            "INSERT INTO processed_events (event_id) VALUES (%s) ON CONFLICT DO NOTHING",
            [event.id]).rowcount
        if inserted == 0:
            return            # duplicate: already handled
        send_eticket(event.order_id)    # NOTE: if this call is itself an external
                                        # effect, it needs its own idempotency key