АналитикаСистемный анализСистемный аналитик

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

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

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

API должен возвращать результат по каждому объекту, а не только один общий статус пакета. Для каждого элемента нужно указать идентификатор, итог обработки, причину ошибки и признак возможности повторной попытки; общий результат должен явно показывать, что пакет выполнен частично.

Исторический контекст

Пакетные операции появились как способ уменьшить число сетевых обращений и ускорить обработку множества независимых объектов. Однако один общий ответ вроде «операция завершена с ошибкой» не показывает, какие элементы успешно обработаны, а какие нет.

На практике возник компромисс между атомарной обработкой всего пакета и независимой обработкой элементов. Контракт API должен заранее определить, какой из этих режимов используется и как клиент узнаёт результат.

Постановка проблемы

Пакет из нескольких объектов может завершиться неодинаково: часть записей обработана, часть отклонена из-за ошибок данных, а часть не обработана из-за временного сбоя зависимости. Если вернуть только общий код ответа, клиент не сможет отличить уже выполненные элементы от тех, которые нужно повторить.

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

Подробное решение

Контракт должен содержать общий итог пакета и массив независимых результатов. Для каждого элемента полезны следующие данные:

  • стабильный идентификатор элемента из запроса;
  • статус обработки: успешно, отклонено, не обработано или принято в асинхронную обработку;
  • машиночитаемый код причины;
  • человекочитаемое описание для диагностики;
  • признак временной ошибки или допустимости повторной попытки.

Общий HTTP-статус не заменяет такие результаты. Его смысл должен быть явно закреплён в контракте: например, успешный ответ может означать, что запрос принят и результаты доступны в теле, а ошибка — что не принят сам пакет целиком. Статус 207 Multi-Status возможен в совместимом контракте, но его нельзя использовать без документированной семантики и поддержки клиентами.

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

Для безопасного продолжения обработки клиент должен повторять только элементы с временной ошибкой или статусом «не обработано». Постоянные ошибки валидации повторять бессмысленно. Кроме того, контракту может потребоваться ключ операции или элемента, позволяющий серверу распознать повтор уже принятого действия; это особенно важно для побочных эффектов.

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

Ситуация из практики

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

Рассматривались три варианта. Полная атомарность упрощала понимание результата, но заставляла повторять весь пакет из-за одной ошибки. Один общий статус без детализации был проще для сервера, но не позволял безопасно продолжить обработку. Асинхронное задание хорошо подходило для долгой обработки, однако требовало дополнительного механизма получения результатов.

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

Что кандидаты часто упускают

  1. Достаточно ли вернуть один общий код HTTP, например успешный или ошибочный?

Нет. Общий код описывает запрос или общий исход операции, но обычно не раскрывает состояние каждого элемента. Даже при наличии общего статуса тело ответа должно позволять сопоставить каждый результат с исходным элементом и определить дальнейшее действие клиента.

  1. Чем частичный успех отличается от ошибки валидации всего пакета?

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

  1. Почему признака «повторить» недостаточно без классификации ошибки?

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