Что такое идемпотентность
Что проверяют
Понимаете ли вы, почему в распределённой системе повторы неизбежны и как сделать, чтобы они не создавали дубли. Следом просят спроектировать идемпотентный эндпоинт оплаты или объяснить, почему POST не идемпотентен.
Как отвечать
Операция идемпотентна, если повторное выполнение с теми же параметрами оставляет систему в том же состоянии, что и однократное. Речь именно о состоянии, а не об ответе: второй DELETE может вернуть 404, и это не нарушение.
По RFC 9110 идемпотентны GET, HEAD, OPTIONS, PUT и DELETE, а POST и PATCH по спецификации нет. Не путайте с безопасностью: безопасные методы вообще не меняют состояние, а PUT и DELETE меняют, но предсказуемо.
Зачем это нужно: клиент получил таймаут и не знает, прошёл ли запрос. Feign или Spring Retry повторяет вызов, брокер доставляет сообщение повторно по гарантии at-least-once. Способы защиты в Java-сервисе:
- ключ идемпотентности: клиент передаёт заголовок
Idempotency-Key, сервер сохраняет ключ и результат, на повтор отдаёт сохранённый ответ; - уникальное ограничение: ключ пишется в таблицу с
UNIQUE, дубль отсекает сама база, а не проверка в коде; - дедупликация консьюмера: id сообщения Kafka сохраняется в той же транзакции, что и бизнес-изменение;
- условное обновление:
@Versionв JPA илиUPDATE ... WHERE status = 'NEW', чтобы переход состояния срабатывал один раз.
Настройка продюсера enable.idempotence=true, включённая по умолчанию с Kafka 3.0, защищает только от дублей при ретраях самого продюсера в пределах партиции, а не от повторной обработки на стороне потребителя.
Где спотыкаются
Проверяют ключ по схеме «найти, если нет, вставить». Два параллельных запроса одновременно проходят проверку, и дубль всё равно появляется. Нужна уникальность в базе или блокировка.
Называют PUT идемпотентным по определению, хотя их реализация увеличивает счётчик. Идемпотентность обеспечивает код, а не название метода.
Включают ретраи на POST в HTTP-клиенте без ключа идемпотентности и получают двойные списания.