Вопрос 5 · Раздел 6

Что такое идемпотентность (idempotency)?

Проще: первый вызов делает работу, все последующие — ничего нового не происходит.

Версии по языкам: English Russian Ukrainian

Junior Level

Идемпотентность — это свойство операции, при котором повторный вызов с теми же параметрами не изменяет результат после первого выполнения.

Проще: первый вызов делает работу, все последующие — ничего нового не происходит.

Простыми словами: сколько бы раз вы ни выполнили операцию, результат будет таким же, как после первого раза.

Примеры из жизни:

  • Идемпотентно: Выключить свет. Сколько раз ни нажми кнопку “выключить” — свет всё равно выключен.
  • Не идемпотентно: Купить билет. Каждая покупка — это новый билет и списание денег.

В контексте HTTP:

  • GET /users/1 — идемпотентен. Сколько раз ни запроси — данные не изменятся.
  • DELETE /users/1 — идемпотентен. Первый раз удаляет, последующие подтверждают, что ресурса нет.
  • POST /users — не идемпотентен. Каждый вызов создаёт нового пользователя.

Зачем это нужно?

Идемпотентность позволяет безопасно повторять запросы при сетевых ошибках, не боясь создать дубликаты.


Middle Level

Математическое определение

$f(x) = f(f(x))$ — результат многократного применения функции равен результату однократного.

// В HTTP идемпотентность касается состояния сервера, а не кода ответа. // Математическая формула здесь — аналогия, а не строгое определение.

Идемпотентные HTTP методы:

  • GET, HEAD, OPTIONS — безопасные и идемпотентные (не меняют состояние)
  • PUT — идемпотентен (полное замещение: status = 'ACTIVE' десять раз = 'ACTIVE')
  • DELETE — идемпотентен (первый вызов удаляет, остальные подтверждают отсутствие)

Неидемпотентные методы:

  • POST — обычно создаёт новые ресурсы. 5 запросов POST /orders = 5 заказов
  • PATCH — зависит от реализации. {"age": 30} — идемпотентен, {"op": "add", "path": "/tags"} — нет

Идемпотентность vs Код ответа

DELETE вернул 200, второй вызов — 404. Это всё равно идемпотентно! Идемпотентность гарантирует идентичность состояния системы, а не кода ответа.

Safe vs Idempotent

Безопасный метод (GET) не меняет состояние. Идемпотентный метод (PUT, DELETE) может менять состояние, но только один раз. Все безопасные методы идемпотентны, но не наоборот.

Паттерн PRG (Post/Redirect/Get)

Решение проблемы неидемпотентности POST в веб-интерфейсах:

  1. Пользователь жмёт “Оплатить” (POST)
  2. Сервер обрабатывает и шлёт 303 See Other
  3. Браузер делает GET на страницу успеха
  4. При F5 повторяется безопасный GET, а не опасный POST

Когда НЕидемпотентность желательна

Аудит-логи, event sourcing, финансовые транзакции — каждая попытка должна быть записана, даже если результат тот же.


Senior Level

Реализация Idempotency Key для POST

В Highload-системах (платежи, заказы) применяется паттерн Idempotency Key:

Алгоритм работы сервера:

  1. Приём: Получаем заголовок Idempotency-Key: <UUID>
  2. Проверка: Ищем ключ в Redis с TTL (например, 24 часа)
  3. Состояния:
    • Ключа нет: Создаём запись со статусом PROCESSING. Выполняем логику. Обновляем на COMPLETED, сохраняем ответ.
    • Статус PROCESSING: Возвращаем 409 Conflict (предотвращает Race Condition)
    • Статус COMPLETED: Возвращаем сохранённый ответ из Redis. Бизнес-логика не запускается повторно.

Distributed Computing

Сеть ненадёжна (Fallacies of Distributed Computing). Запрос может:

  1. Не дойти до сервера
  2. Обработаться, но ответ потеряться
  3. Зависнуть (Timeout)

Идемпотентность позволяет клиенту безопасно повторять (Retry) запрос.

Производительность и Highload

  • Distributed Locks: Redlock добавляет задержку (latency)
  • Storage Cleanup: Хранение ответов за 24 часа может занимать много места в Redis
  • DB Level: INSERT ... ON CONFLICT DO NOTHING (PostgreSQL) — надёжный “последний рубеж”

Edge Cases

  • Если Idempotency-Key тот же, но тело другое — вернуть 400 Bad Request
  • Идемпотентность касается основного ресурса. Побочные эффекты (логирование, статистика) допустимы
  • При тестировании API всегда делайте два одинаковых вызова подряд для проверки

🎯 Шпаргалка для интервью

Обязательно знать:

  • Идемпотентность: повторный вызов с теми же параметрами не меняет результат после первого выполнения
  • Формула: f(x) = f(f(x) — результат многократного применения равен однократному
  • Идемпотентные методы: GET, HEAD, OPTIONS, PUT, DELETE
  • Неидемпотентные: POST (обычно), PATCH (зависит от реализации)
  • Идемпотентность касается состояния сервера, а не кода ответа (DELETE 200 → 404 — всё равно идемпотентно)
  • Safe методы (GET, HEAD) не меняют состояние; все Safe методы идемпотентны, но не наоборот
  • Паттерн PRG (Post/Redirect/Get) решает проблему повторной отправки POST-формы
  • Idempotency Key: UUID в заголовке + Redis-lock для безопасных повторов POST

Частые уточняющие вопросы:

  • Почему DELETE идемпотентен, если второй раз возвращает 404? — Идемпотентность = состояние сервера одинаково, а не код ответа
  • Как сделать POST идемпотентным? — Idempotency-Key в заголовке + хранение результата в Redis
  • Что такое Retry Storm? — Когда тысячи клиентов одновременно повторяют идемпотентные запросы
  • Зачем нужен Distributed Lock при Idempotency Key? — Чтобы предотвратить Race Condition при параллельных запросах

Красные флаги (НЕ говорить):

  • «Идемпотентность означает одинаковый код ответа» — означает одинаковое состояние сервера
  • «POST нельзя сделать идемпотентным» — можно через Idempotency Key + distributed lock
  • «PATCH всегда не идемпотентен» — зависит от операции (replace — идемпотентен, add — нет)
  • «Идемпотентность и Safe — одно и то же» — Safe не меняет состояние, идемпотентный может менять, но один раз

Связанные темы:

  • [[Какие HTTP методы идемпотентны]]
  • [[Почему GET и DELETE идемпотентны]]
  • [[Является ли POST идемпотентным]]
  • [[Какие основные HTTP методы используются в REST]]
  • [[В чём разница между PUT и PATCH]]