Idempotency или идемпотентность — это свойство операции, при котором её повторное выполнение приводит к тому же результату, что и однократное выполнение. Иными словами, сколько бы раз ни отправлялся один и тот же запрос, итоговое состояние системы не изменяется после первого успешного выполнения.
Идемпотентность особенно важна при работе с API, распределёнными системами и платёжными сервисами. Она позволяет безопасно повторить запрос, если произошёл сетевой сбой, истёк тайм-аут или клиент не получил ответ от сервера.
Как работает идемпотентность
Представим, что пользователь отправляет запрос на оплату заказа. Сервер выполняет операцию, но из-за проблем с сетью ответ не доходит до клиента. Клиент не знает, была ли операция успешной, и отправляет запрос повторно.
Если API поддерживает идемпотентность, повторный запрос распознаётся как копия первого. Сервер не создаёт второй платёж, а возвращает результат первоначальной операции.
Схема выглядит так:
- Клиент отправляет запрос с уникальным идентификатором.
- Сервер выполняет операцию и сохраняет её результат.
- Ответ не доходит до клиента или возникает ошибка соединения.
- Клиент повторяет запрос с тем же идентификатором.
- Сервер находит предыдущую операцию и возвращает сохранённый результат без повторного изменения данных.
Идемпотентность не означает, что запрос физически будет выполнен только один раз. Она означает, что повторные одинаковые запросы не вызовут дополнительные нежелательные эффекты.
Идемпотентность в HTTP
Некоторые HTTP-методы считаются идемпотентными по своей семантике:
- GET — получение данных не должно изменять состояние сервера.
- PUT — полная замена ресурса заданным значением. Повторная отправка того же запроса сохраняет тот же результат.
- DELETE — удаление ресурса. После первого удаления повторные запросы не должны удалять что-либо дополнительно.
- HEAD — получение заголовков без тела ответа.
- OPTIONS — получение информации о поддерживаемых возможностях ресурса.
Метод POST обычно не считается идемпотентным: повторная отправка может создать несколько одинаковых записей, заказов или платежей. Однако разработчик может сделать POST-запрос идемпотентным с помощью специального ключа.
Важно отличать идемпотентность метода от фактической реализации API. Например, разработчик может нарушить ожидаемое поведение GET-запроса и изменить данные на сервере. Поэтому идемпотентность определяется не только названием метода, но и логикой конкретного сервиса.
Idempotency Key
Idempotency Key — это уникальный ключ, который клиент передаёт вместе с запросом. Сервер использует его, чтобы определить, является ли новый запрос повтором уже обработанной операции.
Пример запроса:
POST /payments HTTP/1.1
Host: api.example.com
Content-Type: application/json
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
{
"order_id": "12345",
"amount": 1500
}
При первом запросе сервер создаёт платёж и сохраняет результат вместе с ключом. Если клиент повторно отправит запрос с тем же ключом и теми же параметрами, сервер должен вернуть первоначальный результат и не создавать новый платёж.
Если тот же ключ используется с другими параметрами, сервер обычно возвращает ошибку. Это защищает систему от случайного связывания одного идентификатора с разными операциями.
Где применяется
Идемпотентность используют в операциях, повторное выполнение которых может привести к дублированию или финансовым потерям:
- создание платежей;
- оформление заказов;
- бронирование билетов, номеров или услуг;
- списание средств;
- отправка уведомлений;
- создание пользователей и документов;
- загрузка файлов;
- обработка сообщений в очередях;
- взаимодействие между микросервисами;
- повторная обработка событий после сбоя.
Например, при бронировании система может использовать уникальный ключ для каждой попытки оформления. Если пользователь несколько раз нажмёт кнопку оплаты или браузер повторит запрос, система не создаст несколько одинаковых бронирований.
Преимущества
- Безопасные повторы запросов при сетевых сбоях.
- Защита от дублирования платежей, заказов и записей.
- Более высокая надёжность API.
- Упрощение обработки тайм-аутов.
- Предсказуемое поведение распределённых систем.
- Возможность автоматически повторять неудачные запросы.
Ограничения и риски
- Требуется хранить ключи и результаты операций в течение определённого времени.
- Необходимо определить срок действия Idempotency Key.
- Нельзя бездумно повторно использовать один ключ для разных операций.
- Параллельные запросы с одним ключом требуют специальной обработки.
- Хранение всех ответов может увеличивать нагрузку на базу данных.
- Идемпотентность не защищает от ошибок в бизнес-логике и некорректных параметров.
Как реализовать идемпотентность
Типичная реализация включает несколько шагов:
- Клиент создаёт уникальный ключ для каждой бизнес-операции.
- Ключ передаётся в заголовке
Idempotency-Key. - Сервер проверяет, существует ли такой ключ в хранилище.
- Если ключ новый, сервер выполняет операцию.
- Результат операции сохраняется вместе с ключом.
- При повторном запросе сервер возвращает сохранённый ответ.
- Если параметры повторного запроса отличаются, сервер возвращает ошибку.
Упрощённый пример на псевдокоде:
async function createPayment(request) {
const key = request.headers["Idempotency-Key"];
const previousResult = await storage.get(key);
if (previousResult) {
return previousResult;
}
const result = await database.transaction(async () => {
return paymentService.create(request.body);
});
await storage.save(key, result);
return result;
}
На практике проверку ключа и сохранение результата нужно выполнять атомарно. Иначе два одинаковых запроса, пришедших одновременно, могут оба пройти проверку и создать дублирующие операции.
Идемпотентность и повторные попытки
Автоматические повторы часто применяют при следующих ситуациях:
- временная ошибка сети;
- тайм-аут;
- ответ с кодом
429 Too Many Requests; - временная ошибка сервера
5xx; - разрыв соединения до получения ответа.
Повторять запрос следует с тем же Idempotency Key. Если создать новый ключ для каждой попытки, сервер воспримет запросы как разные операции и может выполнить их несколько раз.
При этом не все ошибки нужно повторять. Например, ошибка валидации данных или отсутствие прав доступа обычно не исправляется повторной отправкой того же запроса.
Идемпотентность и другие свойства
Идемпотентность не следует путать с другими характеристиками операций:
- Атомарность — операция выполняется полностью или не выполняется вообще.
- Уникальность — система не допускает дублирование определённых значений.
- Безопасность — операция не изменяет состояние системы.
- Транзакционность — несколько действий выполняются как единая согласованная операция.
Идемпотентная операция может изменять данные. Например, первый запрос DELETE удаляет ресурс, но повторение этого запроса не приводит к дополнительным изменениям.
Итог
Idempotency — это свойство операции сохранять один и тот же итог при многократном выполнении одинакового запроса. Она помогает безопасно обрабатывать повторы, возникающие из-за сетевых сбоев, тайм-аутов и особенностей распределённых систем.
Для операций, которые создают данные или списывают деньги, идемпотентность обычно реализуют с помощью уникального Idempotency-Key. Такой подход предотвращает дублирование и делает API надёжнее.
