Кросс-индустрияИнтеграции и API

API-интеграция: как проектировать сбои, повторы и идемпотентность

Тайм-аут не доказывает, что операция не выполнена. Матрица решений и девять свойств API-контракта помогают архитектору безопасно повторять запросы, исключать дубли и восстанавливать результат после сбоя.

Maria Novikova
NTA's digital editorial persona. Materials are based on the company's expertise and reviewed by the relevant team.
August 14, 2026·5 min read·0 comments
/ Screenshots and video
2 materials

Тайм-аут оставляет результат неизвестным

В понедельник утром корпоративная система отправляет команду на создание заказа. Через три секунды клиент прекращает ждать. В журнале отправителя — тайм-аут, у пользователя — кнопка «Повторить». Но тайм-аут сообщает только одно: клиент не получил ответ вовремя.

Он не сообщает, что произошло на сервере. Запрос мог не дойти. Сервер мог принять его, записать заказ и потерять ответ на обратном пути. Мог сохранить промежуточное состояние и продолжить работу асинхронно. Поэтому автоматическое правило «нет ответа — отправить ещё раз» превращает сетевую неопределённость в два заказа, две заявки или два списания.

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

В русскоязычной документации Yandex Cloud приведён именно этот пограничный случай. Клиент отправил команду, сервер выполнил её, но ответ потерялся. Повтор без ключа может снова изменить ресурс. Повтор с тем же ключом возвращает состояние уже созданной операции.

Первое архитектурное решение поэтому звучит не «сколько раз ретраить», а «как клиент узнает результат после потери ответа».

Идемпотентность начинается с бизнес-операции

Идемпотентность — это не обещание одинаковых HTTP-ответов и не магическое свойство одного заголовка. Это правило: повтор одного и того же намерения не должен второй раз менять бизнес-состояние.

Уровни здесь легко перепутать:

  • HTTP-метод задаёт общую семантику. GET, PUT и DELETE считаются идемпотентными по предполагаемому эффекту, а POST — нет по умолчанию.
  • API-операция уточняет прикладное действие. Например, POST /orders можно сделать безопасным для повтора отдельным контрактом.
  • Бизнес-операция определяет, что считать «тем же самым»: один заказ клиента, одну выплату, одну заявку или один запуск процесса.
  • Техническая попытка — каждый физический запрос, которых у одной бизнес-операции может быть несколько.

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

При этом Idempotency-Key нельзя называть универсально стандартизированным HTTP-заголовком. Соответствующий Internet-Draft IETF истёк 18 апреля 2026 года и не стал RFC. Yandex Cloud и другие провайдеры реализуют собственные договорённости. Значит, имя поля, формат ключа, срок хранения и ответы на конфликт должны быть записаны в документации конкретного API.

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

Матрица решений после ошибки

Код ответа полезен, но не принимает решение в одиночку. Архитектору нужны одновременно четыре условия:

  1. Что известно о результате первой попытки.
  2. Является ли бизнес-операция безопасной для повтора.
  3. Считает ли конкретный API ошибку временной.
  4. Не исчерпан ли общий бюджет времени и попыток.
Состояние клиента Что известно Можно ли повторить Действие
Соединение не установлено Сервер, вероятно, не получил запрос, но транспорт не всегда даёт достаточное доказательство Только по правилам клиента и операции Проверить класс сетевой ошибки; для команды использовать тот же ключ и общий дедлайн
Тайм-аут или разрыв до ответа Результат неизвестен: операция могла завершиться Да, только если операция идемпотентна или защищена ключом Повторить с тем же ключом либо запросить состояние операции
408 Request Timeout Сервер или промежуточный узел не дождался запроса Только если контракт API разрешает Не считать код универсальным разрешением; сохранить тот же ключ
429 Too Many Requests Превышен лимит запросов Обычно да для безопасной операции Уважать Retry-After, применить задержку и не создавать новый ключ
500, 502, 503, 504 Возможна временная серверная или сетевая ошибка; момент изменения состояния не всегда известен Только по контракту API и при идемпотентности Ограниченный повтор с задержкой; при Retry-After ждать указанное время
400, 401, 403 Запрос, аутентификация или права требуют изменения Нет без исправления причины Исправить данные, токен или права; не расходовать бюджет повторов тем же запросом
404 или 409 Значение зависит от предметной модели: ресурс отсутствует или состояние конфликтует Не автоматически Прочитать прикладной код и определить отдельное действие: завершить, перечитать состояние или исправить команду
2xx Сервер принял или завершил операцию Повтор не нужен Сохранить результат; для 202 перейти к проверке состояния по идентификатору операции

RFC 6585 определяет 429 как ограничение частоты и допускает Retry-After. RFC 9110 позволяет серверу тем же полем сообщить задержку при 503. В таблице Yandex Cloud 429 означает превышение лимита, а 503 прямо сопровождается рекомендацией повторить запрос через несколько секунд. Но эта таблица — контракт одного семейства API, а не лицензия повторять любой 500 в любой системе.

Полезное правило: постоянная ошибка должна быстро завершать операцию, временная — давать ограниченный шанс на восстановление, неизвестный результат — вести к тому же ключу или отдельной проверке статуса.

Девять свойств надёжного API-контракта

До реализации критичной команды заполните девять строк. Если хотя бы одна остаётся «решим в коде», поведение при сбое ещё не спроектировано.

Свойство Что зафиксировать Проверка контракта
Единица идемпотентности Какое бизнес-намерение считается одной операцией Две команды с разными намерениями никогда не получают один ключ
Владелец ключа Кто создаёт ключ и когда Клиент создаёт ключ до первой отправки и сохраняет для всех попыток
Область ключа Метод, маршрут, организация, пользователь или другой контекст Одинаковые строки в разных областях не конфликтуют случайно
Отпечаток запроса Какие нормализованные поля связываются с ключом Тот же ключ с другим значимым телом отклоняется как конфликт, а не исполняется
Атомарная защита Где обеспечивается уникальность Конкурентные запросы не проходят проверку одновременно
Состояния операции Например: processing, succeeded, failed Для каждого состояния определён публичный ответ повторному клиенту
Восстановимый результат Код, тело ответа или идентификатор операции Повтор получает известный исход, а не только сообщение «дубль подавлен»
Срок хранения Сколько ключ и результат доступны после завершения Клиент знает окно безопасного повтора и поведение после его истечения
Ошибки и наблюдаемость Повторяемые коды, Retry-After, номер попытки, общий идентификатор Дежурный может связать все попытки с одной операцией без чтения тела запроса

Интервалы и числа нельзя выбирать по шаблону. TTL ключа должен покрывать максимальное окно, в котором клиент вправе повторить команду или восстанавливать результат. Общий дедлайн должен учитывать пользовательское ожидание и SLA зависимого сервиса. Число попыток ограничивается так, чтобы восстановление одного клиента не создавало перегрузку для всех остальных.

Как сервер не допускает дубль

Минимальная серверная последовательность выглядит так:

  1. Проверить запрос и определить область ключа.
  2. Рассчитать отпечаток значимого содержимого.
  3. Атомарно зарегистрировать комбинацию «область + ключ» со статусом processing.
  4. При конфликте сравнить отпечаток и состояние уже известной операции.
  5. Выполнить бизнес-изменение один раз.
  6. Сохранить финальный статус и восстановимый результат.
  7. На повтор вернуть этот результат или понятное состояние незавершённой операции.

В PostgreSQL уникальное ограничение обеспечивает уникальность значения или комбинации значений и создаёт уникальный B-tree индекс. Это хорошая последняя линия защиты от гонки двух запросов. Но индекс не решает весь контракт. Он не знает, что тот же ключ пришёл с другим телом, сколько хранить результат и что отвечать, пока первая попытка ещё выполняется.

Если бизнес-запись и состояние ключа находятся в одной базе, их по возможности фиксируют одной транзакцией. Если операция вызывает внешнюю систему, общей транзакции обычно нет. Тогда нужен явный автомат состояний: зафиксировать намерение, надёжно доставить внешнее действие, записать полученный результат и уметь сверить незавершённые операции. Задача не в обещании «ровно один сетевой запрос», а в доказуемом ограничении «не более одного бизнес-эффекта» и восстановлении неизвестного исхода.

Конкурентный повтор также требует ответа. Возможные договорённости: вернуть 202 со ссылкой на состояние, дождаться первой операции в пределах короткого лимита или сообщить конфликт с тем же идентификатором. Выбор зависит от длительности команды, но он должен быть одинаковым для всех клиентов и описан до разработки.

Повторы должны быть ограничены и видимы

Повтор — это дополнительная нагрузка в момент, когда зависимый сервис уже испытывает проблему. Поэтому клиенту нужны:

  • тайм-аут одной попытки;
  • общий дедлайн всей операции;
  • максимальное число повторов;
  • экспоненциальная задержка со случайным разбросом;
  • поддержка Retry-After;
  • остановка на постоянной ошибке;
  • один уровень, который отвечает за повторы, чтобы несколько библиотек не умножали попытки друг друга.

Официальная стратегия Google Cloud Storage формулирует переносимый принцип: экспоненциальная задержка со случайным разбросом применяется только тогда, когда одновременно выполнены критерий повторяемой ошибки и критерий идемпотентности. Конкретные интервалы и настройки библиотек относятся к продукту Google Cloud; копировать их в корпоративный SLA без расчёта не нужно.

Наблюдаемость должна различать бизнес-операцию и физические отправки. OpenTelemetry рекомендует добавлять http.request.resend_count для каждой повторной отправки. В русскоязычной документации Yandex Cloud Monium каждая попытка оформляется отдельным дочерним спаном, чтобы было видно, какие попытки завершились ошибкой и какая стала успешной.

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

После полезного ответа можно выбирать способ реализации. Каталог решений НТА показывает направления корпоративной автоматизации, а BPMSoft Конструктор — платформенный контур для процессов и расширений. Но платформа не отменяет контракт: тайм-аут, повтор, состояние и восстановление нужно определить для каждого критичного обмена. Если требуется внешняя архитектурная проверка, команда НТА может разобрать одну операцию по этой матрице до начала реализации.

Тест понедельником

Возьмите одну команду с необратимым эффектом: создание заказа, проведение выплаты, регистрацию заявки или запуск процесса. На стенде прервите соединение после отправки запроса, но до получения ответа. Затем отправьте тот же запрос с тем же ключом.

Проверка пройдена, если команда может доказать пять вещей:

  1. В бизнес-данных появился не более чем один результат.
  2. Повтор связан с первой попыткой и не создаёт новую операцию.
  3. Клиент получает сохранённый результат или идентификатор состояния.
  4. В трассировке видны обе физические попытки и один финальный исход.
  5. Дежурный может восстановить состояние без ручного редактирования базы.

Затем повторите сценарий для 429, временного 503, постоянного 400 и конкурентных запросов с одним ключом. Если поведение приходится угадывать по реализации, контракт ещё не готов.

Будущее выдерживает понедельник не тогда, когда API ответил 200 на демонстрации. Оно работает, когда связь оборвалась в неудобный момент, а система всё равно сохранила один результат, объяснила своё состояние и дала безопасный путь восстановления.

Важное уведомление

Материал носит информационный характер и не является индивидуальной консультацией, проектной документацией, инструкцией по внедрению или гарантией результата. Применимость описанных подходов зависит от процессов, систем, данных, требований безопасности и иных условий конкретной организации. Перед внедрением или изменением ИТ‑систем необходимо самостоятельно оценить риски и привлечь профильных специалистов.

«В понедельник утром корпоративная система отправляет команду на создание заказа

— from project discussion
— Takeaways —
  • 01Тайм-аут означает неизвестный результат, а не гарантированный отказ: повтор команды безопасен только при явной идемпотентности или проверке состояния.
  • 02Надёжный контракт связывает один ключ с одним намерением, проверяет тело, атомарно защищает уникальность, хранит исход и описывает конкурентный повтор.
  • 03Коды ошибок, задержка и число попыток зависят от конкретного API и SLA; универсального правила «ретраить все 5xx» нет.
Updated · August 14, 2026

Similar challenge?
Let's map your context.

We will suggest the solutions and modules that fit a similar scenario.

/ What closed the task

Related materials

4 materials
Solution
BPMSoft Studio

A low-code platform for designing business applications, interfaces and processes without extensive manual coding. One environment for business analysts, developers and architects.

Low-code
Case
Индексы PostgreSQL: как проверить пользу для чтения и цену для записи до внедрения

Быстрый SELECT ещё не доказывает пользу индекса. На синтетическом опыте PostgreSQL сравниваем чтение, запись и размер, затем собираем протокол решения до внедрения.

Case
Просроченная дебиторская задолженность: как настроить маршрут ответственности и эскалации в CRM

Просрочка подтверждена, но следующий шаг теряется между продажами и финансами. Разбираем, как назначить владельца, срок действия и маршрут передачи спорного случая в CRM.

Case
Сверка платежей: как проверить календарные границы, повторы и исключения до закрытия периода

Платёж может завершиться до полуночи, а уведомление прийти после неё. Разбираем, как сверять операции по выбранному времени, отличать повтор от новой оплаты и назначать разбор исключений.

Кросс-индустрия
/ Discussion

Be the first to leave a comment

To leave a comment or react, sign in or create an account.