﻿# Повтор POST генерации: идемпотентность нужно определить явно

Как проектировать повтор POST генерации без выдуманной идемпотентности: неоднозначный исход, request ledger, область deduplication и контракт теста.

Автор: Команда Retrivium
Обновлено: 2026-11-01
Канонический URL: https://retrivium.ai/ru/journal/provider-post-retry-idempotency-ledger/
Language: ru

Клиент отправил запрос, но потерял соединение до получения результата. Сервер мог не принять работу, принять её и ещё выполнять либо уже завершить ответ. Эти случаи выглядят одинаково снаружи. Поэтому повтор генерации нельзя автоматически называть безопасным. Если сервис хочет дедуплицировать попытки, ему нужен явный прикладной контракт, а не надежда на одинаковый текст запроса или случайное совпадение идентификатора.

## Начните с семантики метода

[RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html) различает идемпотентные методы и ограничения автоматического повтора неидемпотентной работы. Генерация через POST не становится идемпотентной от использования совместимого API. Нельзя также объявлять поддержку Idempotency-Key у внешней платформы без отдельного документированного основания.

В собственном проекте отделите логическую операцию от транспортной попытки. У попыток разные времена и исходы, но они могут относиться к одному намерению клиента. Не теряйте первоначальный статус при создании новой попытки: именно он определяет, остаётся ли неопределённость о выполненной работе.

## Определите область ключа

Если вводится request ledger, запишите область deduplication: tenant, endpoint, модель либо другая явно выбранная комбинация. Ключ не должен случайно объединять запросы разных клиентов. Сохраните также срок хранения и поведение после его истечения; вечная память не возникает автоматически из наличия одного поля.

Payload fingerprint помогает обнаружить повтор ключа с другим содержимым. Но его состав является частью контракта: какие параметры существенны, как нормализуется JSON и учитываются ли изменения модели. Разные значения нельзя молча считать одним запросом только ради подавления дубликатов в статистике.

## Опишите состояния записи

Минимально различаются неизвестная операция, принятая, выполняемая, завершённая и неуспешная. Для каждой определите поведение повторного запроса: возврат сохранённого результата, ожидание, явный конфликт либо документированная другая ветвь. Это пример проектирования, а не заявление о готовой функции конкретного сервиса.

[OpenAPI](https://spec.openapis.org/oas/v3.1.1.html) позволяет описать внешний интерфейс и формы ответов. Однако схема не обеспечивает транзакционность ledger и не доказывает однократность выполнения. Для реального решения нужны отдельные проверки гонки двух одновременных попыток и восстановления после сбоя процесса между принятием и сохранением итога.

## Проверьте прокси отдельно

[NGINX proxy module](https://nginx.org/en/docs/http/ngx_http_proxy_module.html) описывает повтор upstream и связанные ограничения. Сохраните эффективные настройки, потому что промежуточный слой может добавлять попытки независимо от клиентской логики. Двойной retry на двух уровнях увеличивает число выполнений даже при небольшом локальном лимите каждой стороны.

Fixture использует fake upstream с уникальными attempt IDs и счётчиком принятой работы. Ограниченно разорвите доставку после известной стадии, затем выполните выбранный повтор. Сверьте число логических операций, попыток и выполнений. Одного одинакового ответа недостаточно для доказательства deduplication.

## Сохраните предел гарантии

Нельзя обещать exactly once для всей распределённой цепочки только по успешному локальному тесту. Укажите проверенную область и оставшиеся failure windows. Если исход первой попытки неизвестен, отчёт должен сохранить это состояние, а не дорисовать удобную историю после получения второго ответа.

Связанные материалы — [fallback и лимиты](/ru/journal/router-failover-and-rate-limits/) и [сверка токенов](/ru/journal/router-billing-token-accounting/). Они помогают отделить пользовательский результат от числа фактических попыток и не подменять расчёт выполненной работы наличием одного внешнего request ID.
