Идемпотентные ключи, безопасные и идемпотентные методы, паттерны повторных попыток
Идемпотентность — это свойство операции, при котором повторное выполнение одной и той же операции не приводит к другому результату, чем первое выполнение. В контексте API это критически важно для обеспечения надежности при сетевых ошибках.
| Метод | Безопасный | Идемпотентный | Описание | Пример использования |
|---|---|---|---|---|
| GET | Да | Да | Получение ресурса | GET /users/123 |
| HEAD | Да | Да | Получение заголовков ресурса | HEAD /users/123 |
| POST | Нет | Нет | Создание ресурса или выполнение действия | POST /payments |
| PUT | Нет | Да | Полная замена ресурса | PUT /users/123 |
| PATCH | Нет | Нет | Частичное обновление ресурса | PATCH /users/123 |
| DELETE | Нет | Да | Удаление ресурса | DELETE /users/123 |
| OPTIONS | Да | Да | Получение информации о методах | OPTIONS /users |
| TRACE | Да | Да | Эхо запроса | TRACE /users |
Проблема: При отправке запроса (например, POST /payments) клиент может не получить ответ из-за таймаута и повторить запрос. Это может привести к двойной оплате.
Решение: Клиент генерирует уникальный Idempotency-Key (например, UUID) и отправляет его в заголовке запроса.
Пример заголовка:
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Серверная логика: Сервер хэширует ключ и проверяет, выполнялся ли уже запрос с этим ключом. Если да — возвращает закэшированный ответ. Если нет — выполняет операцию и сохраняет результат с ключом (обычно в Redis с TTL).
HTTP-запрос:
POST /payments HTTP/1.1
Content-Type: application/json
Idempotency-Key: abc123-def456-ghi789
Authorization: Bearer <token>
{
"amount": 99.99,
"currency": "USD",
"customer_id": "cust_123"
}Реализация на сервере (Node.js/Redis):
const redis = require('redis');
const client = redis.createClient();
app.post('/payments', async (req, res) => {
const idempotencyKey = req.headers['idempotency-key'];
if (!idempotencyKey) {
return res.status(400).json({ error: 'Idempotency-Key header is required' });
}
// Проверяем, существует ли уже результат для этого ключа
const cachedResult = await client.get(`idempotency:${idempotencyKey}`);
if (cachedResult) {
// Запрос уже был выполнен, возвращаем закэшированный ответ
return res.status(200).json(JSON.parse(cachedResult));
}
try {
// Выполняем операцию (создание платежа)
const payment = await createPayment(req.body);
// Сохраняем результат в Redis с TTL (24 часа)
await client.setex(
`idempotency:${idempotencyKey}`,
24 * 60 * 60, // 24 часа в секундах
JSON.stringify(payment)
);
res.status(201).json(payment);
} catch (error) {
// В случае ошибки также сохраняем ее, чтобы избежать повторных попыток
await client.setex(
`idempotency:${idempotencyKey}`,
24 * 60 * 60,
JSON.stringify({ error: error.message })
);
res.status(500).json({ error: error.message });
}
});| Этап | Действия | Ответственные | Сроки |
|---|---|---|---|
| Проектирование | Определение операций, требующих идемпотентности | Архитектор, Tech Lead | На этапе проектирования API |
| Разработка | Реализация middleware для обработки Idempotency-Key, интеграция с Redis | Разработчики | В процессе разработки |
| Тестирование | Тестирование сценариев повторных запросов, проверка кэширования | QA, DevOps | Перед PR и в CI |
| Релиз | Документация для клиентов, примеры использования | Technical Writer, Release Manager | При релизе |
| Поддержка | Мониторинг использования ключей, анализ ошибок | DevOps, Support Team | Постоянно |
Q: Что такое идемпотентный ключ и когда его использовать? A: Клиент отправляет уникальный ключ; сервер гарантирует, что запрос с этим ключом выполнен только один раз; важно для платежей, повторных попыток после сетевой ошибки.
Q: Какие HTTP-методы являются идемпотентными по умолчанию? A: GET, HEAD, PUT, DELETE. Повторное выполнение этих методов не изменяет состояние сервера (для PUT и DELETE это верно при условии, что ресурс существует).
Q: Где обычно хранятся идемпотентные ключи на сервере?
A: Redis с TTL — наиболее популярный выбор для хранения идемпотентных ключей из-за его атомарных операций (SETNX, EXPIRE) и высокой производительности. Ключи обычно хранятся с TTL (например, 24 часа), чтобы автоматически очищаться.
Q: Что происходит, если клиент отправляет запрос с уже использованным idempotency key?
A: Если сервер обнаруживает, что запрос с данным Idempotency-Key уже был выполнен, он возвращает тот же самый ответ, который был возвращен при первом выполнении. Это обеспечивает идемпотентность операции.
Использование идемпотентных ключей — это лучшая практика для любых операций, которые могут быть повторены (платежи, создание ресурсов, отправка уведомлений).
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.