Идемпотентность и Idempotency-Key

↑ 5.1 Проектирование REST API · ← Предыдущая · → Следующая

Обязательно~3 мин чтенияУровень: middle

Зачем это на собесе

Ключевая тема для платежей и повторов: «клиент не получил ответ и повторил запрос — что произойдёт?»

Объяснение

Сеть ненадёжна: клиент может не получить ответ и повторить POST. Без защиты создаётся два заказа/платежа.

Idempotency-Key — клиент генерирует уникальный ключ (GUID) для операции и передаёт в заголовке; сервер гарантирует, что операция выполнится один раз, а повторные вызовы получат тот же результат.

sequenceDiagram
  participant C as Клиент
  participant S as Сервер
  participant DB as Хранилище ключей
  C->>S: POST /payments, Idempotency-Key: k1
  S->>DB: вставить k1 (unique)
  S->>S: выполнить платёж
  S->>DB: сохранить ответ для k1
  S-->>C: 201
  C->>S: повтор с k1
  S->>DB: найден k1
  S-->>C: сохранённый ответ 201
public async Task<IResult> Handle(string key, CreatePayment cmd, CancellationToken ct)
{
    var existing = await store.GetAsync(key, ct);
    if (existing is not null) return existing.ToResult();       // повтор: тот же ответ
 
    if (!await store.TryLockAsync(key, ct)) return Results.Conflict("in progress");   // параллельный повтор
 
    var result = await payments.ExecuteAsync(cmd, ct);
    await store.SaveAsync(key, result, ct);
    return result.ToResult();
}

Гарантия достигается unique-индексом на ключ и атомарностью «выполнить + сохранить результат» (одна транзакция или outbox).

Нюансы и подводные камни

  • Ключ привязывайте к пользователю и хэшу тела: тот же ключ с другим телом → 422.
  • Срок хранения ключей ограничен (обычно 24 часа).
  • Параллельные повторы: блокировка по ключу или unique-нарушение → 409.
  • Для естественно идемпотентных операций (PUT, DELETE) ключ не нужен.
  • Идемпотентность потребителя очередей — отдельная тема (Inbox), см. Transactional Outbox и Inbox, идемпотентные консьюмеры.

Практика

  1. Реализуйте middleware/filter идемпотентности с таблицей ключей в PostgreSQL.
  2. Отправьте два одновременных запроса с одним ключом и проверьте результат.
  3. Опишите поведение при падении сервера между платежом и сохранением ответа.

Вопросы с ответами

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