Что такое идемпотентность
Операция идемпотентна, если её результат не меняется от количества выполнений. Стандартный пример: присвоение значения переменной (x = 5) идемпотентно — можно выполнить сто раз, результат тот же. Инкремент (x += 1) — нет.
В контексте API и распределённых систем идемпотентность — это гарантия безопасного повтора при сбоях:
# Идемпотентный запрос — PUT
PUT /cart/items/SKU-1234
{ "quantity": 2 }
→ Повтор даст тот же результат: quantity = 2
# Неидемпотентный запрос — POST
POST /cart/items
{ "sku": "SKU-1234", "quantity": 2 }
→ Повтор создаст вторую позицию: quantity = 4
Почему это важно для e-commerce
В интернет-магазинах каждый запрос проходит через цепочку: браузер → CDN → API-шлюз → бэкенд → база данных. В этой цепочке возникают таймауты, обрывы соединения и перезапросы. Без идемпотентности:
- Дублирование заказов: клиент нажал «Купить», ответ не пришёл, браузер повторил запрос — создался второй заказ.
- Двойное списание: платёжный шлюз получил retry и провёл транзакцию дважды.
- Искажение аналитики: событие
purchaseилиadd_to_cartзасчиталось несколько раз.
Idempotency Key: практическая реализация
Стандартный паттерн — клиент генерирует UUID для каждой операции и передаёт его в заголовке:
POST /orders
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
{ "cart_id": "abc123", "payment_method": "card_xxx" }
Сервер сохраняет (idempotency_key → response) с TTL, обычно 24–48 часов. При повторном запросе с тем же ключом возвращается кэшированный ответ без повторного выполнения операции.
Важно: TTL idempotency key должен быть достаточным, чтобы покрыть типичное время retry — обычно несколько минут. Но не вечным — иначе разрастётся хранилище.
Идемпотентность в платформах персонализации
Для систем рекомендаций и A/B тестирования важны два аспекта:
| Сценарий | Риск без идемпотентности | Решение |
|---|---|---|
Event API (view, purchase) |
Дублирование увеличивает вес товара в профиле | Дедупликация по event_id + временное окно |
| Конверсионное событие в A/B тесте | Одна транзакция засчитана дважды в метрике | Unique constraint по (test_id, user_id, order_id) |
| Обновление профиля пользователя | Лишние данные искажают сегментацию | PUT вместо POST для обновлений |
Типичные ошибки интеграции
- Использовать POST вместо PUT для обновлений сущностей — делает операцию принципиально неидемпотентной
- Не передавать idempotency key при платёжных операциях
- Обрабатывать retry в коде клиента без дедупликации на сервере
- Не предусматривать
event_idв схеме событий трекинга — потом добавить без миграции сложно