Idempotency — Two Clicks, One Charge
Design principles: Keep orders and events consistent with a transactional outbox
In one line
Leave the order and the event to be published in the same DB transaction, and let the consumer handle duplicates in delivery.
Why this was needed
If the process dies right after the order is saved to the DB and the event cannot be published, other services may never learn that the order was created. Conversely, if you send the event first, then even if saving the order fails, other services may process an order that does not exist. Writing to two systems cannot be solved by the order of two lines of a function. A pattern in which the order and an outbox row are saved atomically inside the same DB, and then a separate publisher reads unsent events, reduces this gap.
How it works
BEGIN → orders INSERT → 장애 지점 → outbox INSERT → COMMIT
↓
발행기: pending → publish → sent 표시
소비자: consumed 확인 + 효과 반영 → 같은 트랜잭션으로 COMMIT
If an exception occurs between saving the order and saving the event, neither should exist. If publishing fails, you must not mark it sent. But if the publish succeeded and it died before marking sent, the next run sends the same event again. So this is not exactly-once delivery. In a design where an event can be delivered at least once, to prevent duplicate effects the consumer also ties the recording of the event id and the reflection into the total into a single transaction.
What it looks like in the field
If an idempotency key is reused with a changed amount, you must report a conflict instead of returning the earlier success. In this lab, the order id is the event id, and only one creation event is made per order. To include general update and cancel events, a separate event id and a version and ordering policy are needed. It is set up on the premise of a single publisher. Claims, leases, poison message isolation, and outbox cleanup with multiple publishers are operational tasks that this lab does not implement.
What you will do in the next lab
You will open the DB file to check the row counts and the payload, and inject an exception in the middle. At the end, you will create a situation where the publisher receives an exception right after the consumer has already processed, and then resend. The delivery record is twice, but the sales total must be once. Writing the phrase "deduplication" in the report does not pass. It is judged by the actual stored state read over a separate connection.
Reference: SQLite transactions