Gymterview
middle

Что такое идемпотентность операций?

Идемпотентность – свойство операции, при котором многократное выполнение с одними и теми же входными данными даёт тот же результат, что и однократное выполнение. Как нажатие кнопки “вызвать лифт” – сколько ни нажимай, лифт приедет один раз.

Пример
Идемпотентная операция:
  f(x) = f(f(x))

  Перевести статус заказа в "ОПЛАЧЕН" -- можно вызвать 100 раз, результат один.
  GET /api/accounts/123 -- сколько ни вызывай, данные не изменятся.

НЕ идемпотентная операция:
  "Пополнить счёт на 1000 руб." -- при повторном вызове сумма удвоится!

HTTP-методы и идемпотентность

Метод Идемпотентный Безопасный
GET Да Да
HEAD Да Да
PUT Да Нет
DELETE Да Нет
POST Нет Нет
PATCH Нет Нет

Почему это критично

  • Сетевые сбои – клиент не получил ответ и повторяет запрос.
  • Retry-политики в микросервисах – повторные попытки при таймауте.
  • Дублирование сообщений в очередях (at-least-once delivery в Kafka).
  • В финансовых системах повторный вызов может привести к двойному списанию или зачислению.

Способы обеспечения идемпотентности

1. Idempotency Key

Клиент передаёт уникальный ключ. Сервер проверяет, обрабатывалась ли операция с таким ключом, и при повторном запросе возвращает сохранённый результат.

Пример кода
@PostMapping("/payments")
public ResponseEntity<PaymentResult> createPayment(
        @RequestHeader("Idempotency-Key") String idempotencyKey,
        @RequestBody CreatePaymentRequest request) {

    // Проверяем, не обрабатывали ли мы уже этот запрос
    Optional<PaymentResult> existing = idempotencyStore.find(idempotencyKey);
    if (existing.isPresent()) {
        return ResponseEntity.ok(existing.get());  // возвращаем прежний результат
    }

    PaymentResult result = paymentService.create(request);
    idempotencyStore.save(idempotencyKey, result);
    return ResponseEntity.status(HttpStatus.CREATED).body(result);
}

2. Условные операции

Использование версий, ETag или условных обновлений в БД:

Пример
-- Идемпотентное обновление: статус изменится только один раз
UPDATE payments SET status = 'COMPLETED'
WHERE id = :id AND status = 'PENDING';

3. Естественная идемпотентность

Проектирование операций как установка конечного состояния, а не как приращение:

Пример
// Не идемпотентно:
account.addBalance(1000);  // при повторе удвоится

// Идемпотентно:
account.setBalance(5000);  // при повторе результат тот же

Итог

При проектировании API и обработчиков событий всегда задавайте вопрос: “Что произойдёт, если эта операция выполнится дважды?” Если ответ “ничего страшного” – операция идемпотентна. Если нет – нужен один из описанных механизмов защиты.

На собеседовании: Интервьюер хочет услышать конкретные способы обеспечения идемпотентности (Idempotency Key, условные обновления), а не только определение. Частая ошибка – утверждать, что DELETE всегда идемпотентен, забывая о побочных эффектах (события, триггеры).