Почему GET и DELETE идемпотентны?
Метод GET не изменяет данные на сервере. Сколько раз вы ни запросите один и тот же ресурс — результат будет одинаковым.
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: Ресурс существует. Удаляем. Код
200или204. - Запрос 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 дизайн]]