Duplicate Suppression Is End-to-End — One Retry, Three Hops the same transfer is retried at every layer — only an ID carried from client to database stops it twice time not to scale ZONE 1 · hop 1: TCP duplicate suppression ZONE 2 · hop 2: the timeout gap ZONE 3 · the database: constraint does the job one TCP connection: app → DB app DB SYN · original retry ✗ dropped TCP suppresses duplicates within ONE connection conn breaks → new conn: TCP cannot help browser server POST /transfer req=0286… COMMIT sent, reply lost — timed out? retry: same POST req=0286… retry = separate request, separate transaction app DB INSERT req=… ✓ same req ID ✗ UNIQUE(request_id) rejects the dup — 2nd tx aborts moved $11 once, not $22 Example 13-1 — NOT idempotent: a retry moves $22, not $11 BEGIN TRANSACTION; UPDATE accounts SET balance = balance + 11.00 UPDATE accounts SET balance = balance - 11.00 COMMIT; -- to 1234 / from 4321 retried after timeout ⇒ $22 moved instead of $11 Example 13-2 — idempotent: UNIQUE(request_id), INSERT-first ALTER TABLE requests ADD UNIQUE (request_id); BEGIN TRANSACTION; INSERT INTO requests VALUES('0286FDB8…', 4321, 1234, 11.00); UPDATEs + COMMIT; -- 2nd INSERT fails, tx aborts duplicate rejected ⇒ $11 moved once, not $22 TCP dedup (one connection) ⊊ DB transactions (one hop) ⊊ request ID, client → database (end to end) each inner layer narrows duplicates — but only the end-to-end ID solves it request retry / 2nd attempt ✗ rejected reply lost / timeout sources: Example 13-1 ($11/$22, accounts 1234/4321) · Example 13-2 (request_id 0286FDB8…, INSERT-first transaction) Saltzer–Reed–Clark (1984): duplicate suppression can only be solved end to end — lower layers only reduce the odds