0001: nonce idempotency
ADR-0001: Идемпотентность сообщений через nonce + enforce_nonce
Дата: 2026-01-26 Статус: Accepted
Контекст
Клиентские ретраи (таймауты, повторная отправка, нестабильная сеть) приводят к созданию дублей сообщений: один и тот же “логический” send превращается в несколько записей в БД.
Нам нужно поведение, близкое к Discord: идемпотентность по nonce включается явно флагом enforce_nonce и действует ограниченное время (окно “несколько минут”), а не навсегда.
Также важно, чтобы HTTP и WebSocket давали одинаковый результат: WebSocket не должен обходить защиту от дублей.
Решение
Принять контракт
nonce + enforce_nonceдля создания сообщений:
nonce— идентификатор попытки отправки (короткая строка/число).enforce_nonce— включает идемпотентность поnonce.
Реализовать дедупликацию через Redis с TTL:
Использовать ключ вида
nonce:{author}:{nonce}.Ставить ключ атомарно с TTL (окно идемпотентности).
При повторе с тем же
nonceвозвращать уже созданное сообщение (по сохранённомуmessage_id).
Централизовать создание сообщений:
HTTP и WebSocket должны вызывать один общий сервис создания сообщения, чтобы правила идемпотентности применялись одинаково.
Почему так
Уникальный индекс в Postgres даёт “вечную” идемпотентность, а нам нужно окно по времени.
Redis с TTL естественно моделирует “несколько минут” и сам очищает ключи.
Атомарная постановка ключа защищает от гонок при параллельных запросах.
Единый сервис исключает расхождение логики между HTTP и WebSocket.
Последствия
Для enforce-режима появляется зависимость от Redis (нужно отражать в инфраструктуре и проверках здоровья).
Нужно поддерживать одинаковую схему обработки в двух транспортных слоях (HTTP/WS).
В логах/трассировке полезно хранить
nonceу сообщения (для отладки и расследования повторов).
Последнее обновление