Правила идемпотентности – HTTP, REST API, Idempotency Key и ETag
Этот материал представляет собой объяснение понятия идемпотентности, включая контрольные вопросы и теги.
В двух словах
Идемпотентность означает: одинаковую операцию можно выполнять сколько угодно раз с одним и тем же результатом. Формально: f(f(x)) = f(x).
Краткое техническое определение
Идемпотентность – это свойство операции, а не endpoint’а. Она стабилизирует результат при повторениях. В HTTP GET, HEAD, PUT, DELETE по спецификации идемпотентны, POST не является таковым, но можно сделать его идемпотентным с помощью Idempotency Key. На практике идемпотентность означает, что повторения не создают дополнительных побочных эффектов (нет дублей заказов, нет двойного списания). Технически это достигается через детерминированные переходы состояния, проверку версии с помощью ETag и дедубликирующую логику на сервере. В распределённых системах с гарантией доставки хотя бы один раз повторные попытки обычны, а идемпотентность делает их безопасными.
Ключевые моменты для проверки
- Определение: f(f(x)) = f(x), идемпотентность как свойство операции, а не пути
- Соответствие HTTP: GET/HEAD безопасны и идемпотентны, PUT/DELETE идемпотентны, POST не идемпотентен
- POST-идемпотентность с Idempotency Key, hash запроса, дедубликирующим хранилищем, детерминированными ответами
- Коды состояния и заголовки: 201 с Location при первой попытке, повторение возвращает 200/201 согласованно, 412 при конфликте If-Match
- Отслеживаемость побочных эффектов, логирование ID корреляции, задокументированные правила повтора
- Нет конфиденциальных данных в ошибках, защита от replay через ограниченные по времени ключи и подписи
- Меньше двойных списаний и обращений в поддержку, надёжные интеграции, чётко определённая политика повтора
- OpenAPI описывает идемпотентность, каталог ошибок, время жизни Idempotency Keys, примеры ответов
Основные компоненты
- Формальное определение и отличие от свойства безопасности
- Семантика методов в HTTP: GET, HEAD, PUT, PATCH, DELETE, POST
- Условные запросы с ETag, If-Match, If-None-Match
- Дедубликация: хранилище, ключ к ответу, отображение с временем жизни
- Детерминированные переходы состояния и обнаружение конфликтов (409, 412)
- Генерация Idempotency Key: клиент создаёт стабильный ключ для каждого бизнес-события
- Patern Outbox/Inbox для надёжных побочных эффектов (события, платежи)
- Стратегия повтора: экспоненциальная задержка, уважение к 429 и Retry-After
- Наблюдаемость: ID корреляции, отслеживание идемпотентных операций
- Тестирование: тесты повторений, хаос-тесты, тесты параллелизма и race-условий
Практический пример
// Идемпотентный POST для платежа с Idempotency Key и ETag
POST payments
Headers: Content-Type: application/json, Idempotency-Key: pay-123-abc
Body: { orderId: 42, amount: 59.98, method: "card" }
Логика сервера:
if exists DedupeStore[key: pay-123-abc]:
return storedResponse
else:
if PaymentAlreadyConfirmed(orderId: 42):
resp = { id: "P9001", status: "confirmed" }, code: 200
else:
create Payment(id: "P9001", status: "confirmed")
resp = { id: "P9001", status: "confirmed" }, code: 201, headers: ETag: W/"r77"
DedupeStore.save(key: pay-123-abc -> resp with ttl: 24h)
return resp
Повторение с тем же ключом:
Клиент вновь отправляет POST с Idempotency Key, сервер возвращает тот же ответ, 201 при первой попытке, 200 при более поздних повторениях, никогда – двойное списание.
Преимущества и недостатки
Преимущества
- Безопасные повторы при сетевых ошибках
- Защита от двойного списания
- Более чёткая модель ошибок
- Более надёжные интеграции
- Лучший пользовательский опыт
Недостатки
- Дополнительное хранилище и затраты на управление дедубликацией
- Более сложная логика побочных эффектов
- Требуется точное определение времени жизни и семантики
- Возможны блокировки или конфликты при параллелизме
Типичные контрольные вопросы (с краткими ответами)
- Отличие между безопасностью и идемпотентностью? Безопасность = без изменения состояния на сервере (GET), идемпотентность = множественное выполнение без дополнительных побочных эффектов (PUT/DELETE).
- Как сделать POST операции идемпотентными? Idempotency Key на бизнес-событие, дедубликирующее хранилище с отображением ключ→ответ, детерминированная логика, согласованные коды состояния, ограниченное время действия.
- ETag и If-Match в контексте идемпотентности? Защищают от потерянных обновлений, обновление только с известной версией, иначе 412 Precondition Failed.
- DELETE действительно идемпотентен? Первый DELETE возвращает 200/204, более поздние DELETE могут возвращать 204/404, состояние системы остаётся неизменным (ресурс удалён).
- Типичные коды состояния при идемпотентности и повторах? 200/201/204 при успехе, 409 при бизнес-конфликте, 412 при нарушении предусловия, 429 при ограничениях + Retry-After, 5xx при временных ошибках сервера.
- Как определить время жизни Idempotency Key? По бизнес-логике до завершения события, технически как временное окно (например, 24 часа), задокументировано в API.
- Риски отсутствия идемпотентности в распределённых системах? Двойные побочные эффекты (двойные платежи), несогласованные состояния, ручные исправления, повышенные затраты на поддержку.
- Идемпотентность с Event Sourcing и Outbox? Запись события в Outbox (одна транзакция), надёжная публикация, потребитель с Inbox для ID обработанных событий, идемпотентный обработчик.
Основные источники
- https://www.rfc-editor.org/rfc/rfc9110
- https://www.rfc-editor.org/rfc/rfc7807
- https://docs.stripe.com/idempotency



