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

Почему GET и DELETE идемпотентны?

Метод GET не изменяет данные на сервере. Сколько раз вы ни запросите один и тот же ресурс — результат будет одинаковым.

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

Junior Level

GET — идемпотентен, потому что только читает

Метод GET не изменяет данные на сервере. Сколько раз вы ни запросите один и тот же ресурс — результат будет одинаковым.

GET /users/1  →  Вернёт данные пользователя
GET /users/1  →  Снова вернёт те же данные
GET /users/1  →  И снова те же данные

GET называется “безопасным” (Safe) — он не меняет ресурс, к которому обращается клиент. GET может писать в лог или обновлять счётчик просмотров в Redis. Это допустимо! “Безопасный” в HTTP означает “не влияет на ответ, который получит клиент”.

DELETE — идемпотентен, потому что результат одинаков

Первый DELETE удаляет ресурс. Все последующие DELETE подтверждают, что ресурса уже нет. Итоговое состояние сервера (“ресурса нет”) не меняется.

DELETE /users/1  →  200 OK (ресурс удалён)
DELETE /users/1  →  404 Not Found (ресурса уже нет)
DELETE /users/1  →  404 Not Found (ресурса всё ещё нет)

// Разные коды ответа (200 vs 404) не нарушают идемпотентность, потому что
// идемпотентность — про состояние ресурса на сервере, а не про то, что видит клиент.
// Ресурс удалён после первого вызова — последующие ничего не меняют.

После всех вызовов состояние сервера одинаковое — пользователя нет.


Middle Level

GET: Идемпотентность через Безопасность

Согласно RFC 9110, безопасные методы не имеют побочных эффектов, которые клиент бы ожидал.

Скрытые изменения: GET может писать в лог или обновлять счётчик просмотров в Redis. Это допустимо! Идемпотентность и безопасность касаются ответственности за ресурс. Технические побочные эффекты (логи, аналитика, прогрев кеша) не нарушают контракт GET.

Риск: Если реализовать GET /user/1/delete, браузер может “предзагрузить” ссылку и случайно удалить пользователя. Именно поэтому GET обязан быть безопасным.

DELETE: Идемпотентность через Инвариантность

DELETE меняет состояние, поэтому не безопасен, но идемпотентен.

Механика:

  1. Запрос 1: Ресурс существует. Удаляем. Код 200 или 204.
  2. Запрос 2: Ресурса нет. Пытаемся удалить. Код 404.

Ключевой момент: Итоговое состояние сервера (“Ресурса нет”) не изменилось после второго вызова.

Мягкое удаление (Soft Delete)

Если DELETE ставит флаг is_deleted = true и обновляет deleted_at — он всё равно остаётся идемпотентным. Повторный вызов просто ещё раз установит true.

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

  • Результаты DELETE не кешируются, но DELETE инвалидирует кеш для этого URI
  • Для пакетного удаления используйте DELETE /resources?ids=1,2,3 вместо POST /delete-multiple (второе не идемпотентно)

Диагностика

  • Огромное количество 404 на DELETE запросах указывает на агрессивные Retry-политики клиента
  • При тестировании всегда делайте два одинаковых вызова DELETE подряд

Senior Level

Удаление в распределённых системах

В микросервисах DELETE может инициировать цепочку событий (например, через Kafka).

Edge Case: Первый DELETE удалил запись и отправил событие UserDeleted. Второй DELETE (Retry из-за лага сети) пришёл позже — сервер не должен отправлять событие UserDeleted второй раз, иначе downstream-сервисы могут сбоить.

Решение: Проверка существования ресурса перед удалением и отправкой события.

Caching DELETE

DELETE инвалидирует (сбрасывает) кеш для этого URI на всех промежуточных узлах. Это гарантирует, что следующий GET не вернёт старые данные из кеша.

Batch Delete: POST /delete-multipleне идемпотентно. Для идемпотентности используйте DELETE /resources?ids=1,2,3.

Идемпотентность и распределённые события

При использовании очередей (Kafka, RabbitMQ) DELETE должен гарантировать, что событие удаления отправляется только один раз, даже при повторных вызовах.

GET: результат одинаков, потому что ничего не меняется.
DELETE: результат одинаков, потому что после первого вызова менять уже нечего.

Мониторинг

  • Отслеживайте количество 404 ответов на DELETE запросы
  • Высокий процент 404 на DELETE = агрессивные Retry-политики на клиенте
  • При интеграции с event-driven архитектурой проверяйте, что повторный DELETE не генерирует дубликаты событий

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

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

  • GET идемпотентен, потому что только читает — Safe метод по RFC 9110
  • GET может писать в логи и кеш — это допустимо, «безопасность» касается ответа клиенту
  • DELETE идемпотентен: после первого вызова ресурса нет, повторные ничего не меняют
  • Разные коды ответа (200 → 404) не нарушают идемпотентность — важно состояние сервера
  • DELETE инвалидирует кеш для данного URI, чтобы следующий GET не вернул старые данные
  • Soft Delete (is_deleted = true) остаётся идемпотентным — повторный вызов просто ставит true ещё раз
  • DELETE в распределённых системах не должен отправлять событие удаления повторно

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

  • Почему GET /user/1/delete — это плохо? — Браузеры могут предзагружать GET-ссылки, что случайно удалит пользователя
  • Как обеспечить идемпотентность пакетного удаления? — DELETE /resources?ids=1,2,3, а не POST /delete-multiple
  • Что если DELETE вызван дважды в event-driven системе? — Второй вызов не должен генерировать дубликат события
  • GET обновляет счётчик просмотров — это нарушает идемпотентность? — Нет, технические побочные эффекты допустимы

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

  • «DELETE не идемпотентен, потому что второй раз возвращает 404» — состояние сервера одинаково
  • «GET должен быть абсолютно без побочных эффектов» — логи, аналитика, прогрев кеша допустимы
  • «Для пакетного удаления лучше POST» — DELETE с query-параметрами идемпотентен, POST — нет
  • «Разные коды ответа = разная идемпотентность» — идемпотентность про состояние, не про код ответа

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

  • [[Что такое идемпотентность (idempotency)]]
  • [[Какие HTTP методы идемпотентны]]
  • [[Является ли POST идемпотентным]]
  • [[Какие основные HTTP методы используются в REST]]
  • [[Что такое RESTful API дизайн]]