Идемпотентность

Базовый System Design
≈ 2 мин чтения

Повтор не должен ломать состояние

В распределённой системе невозможно исходить из предположения, что каждый запрос или сообщение придёт строго один раз. Клиент может повторить запрос после таймаута, а брокер — доставить сообщение повторно, если не получил подтверждение обработки.

Идемпотентная операция допускает повтор с тем же смыслом без дополнительного изменения результата. Например, повторно установить статус заказа в CANCELLED обычно безопаснее, чем дважды выполнить команду «списать 500 рублей».

Идемпотентный ключ отличает повтор от новой операции

Для операций создания или оплаты клиент может прислать уникальный ключ. Сервис сохраняет результат первого выполнения и при повторе с тем же ключом возвращает его снова, не выполняя бизнес-действие второй раз.

POST /payments
Idempotency-Key: 8f76c0d2-...

{
  "orderId": 421,
  "amount": 500
}

Ключ должен относиться к одной логической операции и храниться достаточно долго, чтобы покрыть реальное окно возможных повторов.

Потребители сообщений тоже должны учитывать повторы

Сообщение удобно снабжать стабильным eventId. Перед выполнением необратимого действия consumer может проверить, не был ли этот идентификатор уже обработан, а затем сохранить факт успешной обработки вместе с бизнес-изменением настолько атомарно, насколько позволяет выбранное хранилище.

eventId = evt-42

first delivery  -> process -> remember evt-42
second delivery -> already processed -> skip

Повтор ответа и повтор эффекта — разные вещи

Операция может вернуть тот же успешный результат повторно, но главное — не выполнить повторно опасный побочный эффект: не списать деньги ещё раз, не создать второй заказ и не начислить бонус дважды. Именно это делает ретраи безопасными для распределённой системы.

Повторы — нормальная часть распределённого взаимодействия. Надёжный дизайн не пытается доказать, что дублей никогда не будет, а делает критичные операции безопасными при повторной доставке.