The server commits an impression. The phone goes through a tunnel before the acknowledgement arrives. When the connection comes back, the phone still has an unsent event in its queue. The advertiser’s invoice now depends on one small implementation detail: when did the client assign the event ID?
Assign it when the display first qualifies. Store it alongside the queued event. If the phone generates a fresh ID inside the retry loop, the server has no way to distinguish a second delivery attempt from a second display.
Try the lost acknowledgement in PostgreSQL
This example uses a temporary table. Run the statements in one database session. The two inserts are the two deliveries of the same display.
CREATE TEMP TABLE counted_displays (
publisher text,
display_id text,
PRIMARY KEY (publisher, display_id)
);
INSERT INTO counted_displays VALUES ('demo', 'turn-42-card-1')
ON CONFLICT DO NOTHING RETURNING display_id;
INSERT INTO counted_displays VALUES ('demo', 'turn-42-card-1')
ON CONFLICT DO NOTHING RETURNING display_id;
The first insert returns one row. The retry returns zero. Both requests can receive a successful acknowledgement. If the reporting pipeline increments a counter on every received request, the invoice will still be wrong. Count the committed rows, or queue the accounting event in the same transaction as the successful insert. PostgreSQL documents the conflict and RETURNING semantics here.
One question remains for the publisher. If someone scrolls back to yesterday’s chat and sees the same card again, does that qualify as a new impression? Agree on that before implementing replay. A component that generates a new display ID every time it mounts has already made the decision for you.