В КУРСЕ?

Разбираемся в теме

Заказ принят дважды? Как микросервисам пережить повтор сообщения

Покупатель нажал оформить заказ, ответ задержался, и приложение повторило запрос. Одновременно сообщение о резервировании товара могло повторно прийти из очереди. Если каждое получение считать новым намерением купить, система создаст лишние заказы или повторно уменьшит доступный остаток. При проектировании API на C# важно заранее определить, какие действия относятся к одной операции и как сервисы узнают уже обработанное сообщение.

Принять запрос не значит завершить весь заказ

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

Повтор должен узнавать ту же операцию

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

Событие тоже нужно сохранить надёжно

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

Попробуйте на практике

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

  1. Нарисуйте два сервиса и задайте исходное состояние: доступно три единицы товара, заказ просит одну, его статус ожидает резервирования.
  2. Создайте карточку сообщения с постоянным идентификатором, номером заказа и количеством. После первой обработки отметьте резерв одной единицы и сохранённый результат.
  3. Передайте ту же карточку повторно. Покажите, что получатель находит сохранённый результат, а доступное количество остаётся равным двум.
  4. Рассмотрите сбой между сохранением заказа и отправкой события. Добавьте исходящий журнал в локальную транзакцию сервиса заказов и объясните, зачем получателю всё равно нужна защита от повторов.

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

Частые вопросы

Идемпотентность означает, что сообщение обязательно придёт один раз?

Нет. Оно может доставляться несколько раз. Свойство относится к результату обработки повторов: повтор той же операции не должен создавать дополнительный предусмотренный эффект.

Достаточно ли написать обработчик на C#, чтобы получить такую защиту?

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

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

Зарегистрируйтесь, чтобы уточнить возможность доступа к этому материалу

Зарегистрироваться
← К списку материалов