Что такое идемпотентный ключ (Idempotency Key)?
Идемпотентный ключ (Idempotency Key) — уникальный идентификатор, передаваемый клиентом в запросе, позволяющий серверу распознавать повторные запросы и не выполнять операцию дважды.
Аналогия из жизни: идемпотентный ключ — как номер платёжного поручения в банке. Если вы случайно отправите одно и то же поручение дважды, банк увидит, что поручение с таким номером уже обработано, и не спишет деньги повторно.
Зачем нужен
Метод POST не является идемпотентным. При сетевых ошибках (таймаут, разрыв соединения) клиент не знает, был ли запрос обработан. Повторная отправка может привести к дублированию (двойное списание денег, двойное создание заказа).
Как работает
Пример
POST /api/payments HTTP/1.1
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
{"amount": 1000, "currency": "RUB", "recipientId": 42}
- Сервер проверяет, обрабатывался ли запрос с таким ключом.
- Если нет — обрабатывает и сохраняет результат.
- Если да — возвращает сохранённый результат без повторного выполнения.
Реализация в Spring
@RestController
@RequestMapping("/api/payments")
@RequiredArgsConstructor
public class PaymentController {
private final PaymentService paymentService;
private final IdempotencyService idempotencyService;
@PostMapping
public ResponseEntity<PaymentResponse> createPayment(
@RequestHeader("Idempotency-Key") String idempotencyKey,
@Valid @RequestBody PaymentRequest request) {
// Проверяем, есть ли уже результат
return idempotencyService.findByKey(idempotencyKey)
.map(cached -> ResponseEntity.ok(cached))
.orElseGet(() -> {
PaymentResponse response = paymentService.processPayment(request);
idempotencyService.save(idempotencyKey, response);
return ResponseEntity.status(HttpStatus.CREATED).body(response);
});
}
}
@Service
@RequiredArgsConstructor
public class IdempotencyService {
private final IdempotencyRepository repository;
public Optional<PaymentResponse> findByKey(String key) {
return repository.findByKey(key)
.map(entry -> deserialize(entry.getResponse()));
}
public void save(String key, PaymentResponse response) {
IdempotencyEntry entry = new IdempotencyEntry();
entry.setKey(key);
entry.setResponse(serialize(response));
entry.setCreatedAt(LocalDateTime.now());
entry.setExpiresAt(LocalDateTime.now().plusHours(24));
repository.save(entry);
}
}
Ключи обычно имеют срок жизни (24 часа) и хранятся в БД или Redis. Паттерн широко используется в платёжных системах (Stripe, Tinkoff, YooMoney).
На собеседовании: ключевое — объяснить, зачем нужен Idempotency Key (для POST-запросов при сетевых ошибках) и как он работает (проверка + сохранение результата). Частая ошибка — путать идемпотентность метода и Idempotency Key.