← Назад в словарь

Идемпотентность

Идемпотентность (Idempotency) — повторяемость без изменения результата

TL;DR

Идемпотентность — свойство операции: повторное выполнение с теми же входными данными не меняет итоговый результат системы. Критично для платежей, заказов и API с повторными отправками.

Краткое определение

Идемпотентность — свойство операции, при котором повторное выполнение с теми же входными данными не меняет финальное состояние системы после первого успешного выполнения.

Оригинал и перевод

  • Язык: английский
  • Оригинал: Idempotency (от лат. idem — тот же + potens — сильный)
  • Буквальный перевод: одинаковый результат при повторении
  • Русские аналоги: идемпотентность, неизменность результата

Синонимы и варианты написания

  • Идемпотентная операция
  • Идемпотентный метод
  • Idempotent operation
  • Повторяемость без побочных эффектов

Где используется

  • HTTP API: методы GET, PUT, DELETE (но не POST по умолчанию)
  • Платёжные системы: защита от двойных списаний
  • Очереди сообщений: повторная обработка без дублирования
  • Базы данных: UPSERT операции (INSERT ... ON CONFLICT UPDATE)
  • Распределённые системы: ретраи при сетевых сбоях

Когда это важно

Идемпотентность критична в следующих сценариях:

  • Повторная отправка запросов при таймаутах, обрывах соединения
  • Ретраи в очередях (RabbitMQ, Kafka) — сообщение может доставляться несколько раз
  • Платежи и заказы — нельзя создать дубль транзакции
  • Синхронизация данных — повторный запрос не должен ломать состояние

Как это работает (принцип)

Пример из математики

f(x) = |x|  (модуль числа)

f(5) = 5
f(f(5)) = f(5) = 5  ← идемпотентно

f(x) = x + 1

f(5) = 6
f(f(5)) = f(6) = 7  ← НЕ идемпотентно

Пример из HTTP

# PUT запрос (идемпотентный)
PUT /users/123/status
{"status": "paid"}

# Первый вызов: статус изменён на "paid"
# Второй вызов: статус остаётся "paid" (результат тот же)
# Третий вызов: статус всё ещё "paid" ✅

# POST запрос (НЕ идемпотентный по умолчанию)
POST /payments
{"amount": 1000}

# Первый вызов: платёж создан #1
# Второй вызов: платёж создан #2 ← дубль! ❌
# Третий вызов: платёж создан #3 ← ещё один дубль!

Реализация идемпотентности для API

Pattern: Idempotency Key

Клиент генерирует уникальный ключ для каждой операции и передаёт в заголовке:

POST /payments
Idempotency-Key: pay_abc123xyz789
Content-Type: application/json

{"amount": 1000, "currency": "RUB"}

Сервер обрабатывает:

// Псевдокод обработчика
function processPayment($request) {
    $key = $request->header('Idempotency-Key');
    
    // Проверяем, есть ли уже результат для этого ключа
    $cachedResult = $redis->get("idempotency:{$key}");
    if ($cachedResult) {
        return $cachedResult;  // Возвращаем кэшированный результат
    }
    
    // Блокируем обработку этого ключа (защита от race condition)
    $lock = $redis->set("lock:{$key}", "processing", ['NX', 'EX' => 30]);
    if (!$lock) {
        wait_and_retry();
    }
    
    // Выполняем платёж
    $result = executePayment($request->body);
    
    // Кэшируем результат (24 часа)
    $redis->setex("idempotency:{$key}", 86400, json_encode($result));
    
    return $result;
}

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

МетодИдемпотентный?Почему
GET✅ ДаТолько чтение, не меняет состояние
PUT✅ ДаЗамена ресурса целиком, результат одинаковый
DELETE✅ ДаУдаление: первый раз удаляет, последующие — ничего не делают
PATCH⚠️ ЗависитЕсли применяется к состоянию — может быть неидемпотентным
POST❌ НетСоздание нового ресурса, каждый вызов = новый объект

Типичные ошибки при реализации

  • Нет Idempotency-Key — клиент не может безопасно повторить запрос
  • Кэш результата слишком короткий — при ретрае ключ уже удалён
  • Генерация ключа на сервере — клиент должен сам генерировать ключ
  • Идемпотентность только по URL — разные тела запроса = разные операции
  • Отсутствие блокировки — race condition при параллельных запросах

Аналоги и связанные термины

  • Retry — механизм повторных попыток (требует идемпотентности)
  • Exactly-once semantics — семантика «ровно один раз» (идеал, трудно достижим)
  • At-least-once semantics — «как минимум один раз» (требует идемпотентности)
  • Deduplication — устранение дубликатов
  • Optimistic locking — оптимистическая блокировка через версии

Смотри также (статьи на сайте)

Смотри также (сниппеты)

Смотри также (термины)

  • Rate limiting — ограничение частоты запросов (дополняет идемпотентность)
  • Backpressure — обратное давление в потоках
  • ACID — атомарность транзакций (связано с надёжностью)

Мини-FAQ

Идемпотентность и кэш — одно и то же?

Ответ: Нет. Идемпотентность — про одинаковый результат при повторении операции. Кэш — про хранение ответа для ускорения. Но кэш часто используют для реализации идемпотентности (кэширование результата по ключу).

POST можно сделать идемпотентным?

Ответ: Да, через Idempotency-Key. Но по семантике REST POST создаёт новый ресурс, поэтому лучше использовать PUT для идемпотентного обновления.

Обязательно ли хранить результат 24 часа?

Ответ: Зависит от сценария. Для платежей — желательно долго (клиент может повторить через часы). Для некритичных операций — достаточно 5-15 минут.

Что делать, если клиент не передаёт Idempotency-Key?

Ответ: Для критичных операций (платежи) — требовать ключ обязательно (возвращать 400 Bad Request). Для некритичных — можно генерировать временный ключ на основе хэша тела запроса + timestamp.

Идемпотентность замедляет систему?

Ответ: Минимально. Проверка кэша и блокировка занимают миллисекунды. Это необходимая плата за надёжность в distributed systems.