ADR 0004: Idempotency keys for awards¶
- Status:
Accepted
Context¶
Clients retry award requests on network failure. A naive retry would award the same event twice.
Decision¶
Before inserting, the persistence layer derives an idempotency key from the
first non-empty of eventId, idempotencyKey or correlationId
(app/services/user_points/persistence.py) and short-circuits if a matching
row already exists. The guarantee is enforced at the database by
UniqueConstraint("userId", "taskId", "idempotencyKey")
(app/model/user_points.py, constraint
uq_user_points_user_task_idempotency), so a duplicate can never be
persisted even if the read check races.
Consequences¶
Awards are safe to retry: a repeated request with the same key returns the existing assignment instead of creating a second one.
The dedupe scope is
(user, task, key)- the same key under a different task is a different award. Anullkey is allowed (the un-deduped path).Under a true concurrent retry race, the loser of the unique-constraint race receives
409 Conflictrather than the original success body. The no-double-award guarantee still holds. See Known limitations.
See also¶
Domain Model - “Idempotency & concurrency”.
Integrating with GAME - the idempotency-key request header.
ADR 0003: Atomic points write - the transaction this builds on.