АрхитектураМикросервисы и интеграцииРазработчик интеграционных сервисов

Практическая ситуация: потребитель события падает при появлении нового значения перечисления. Какой принцип...

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

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

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

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

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

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

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

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

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

Допустим, контракт содержит состояния NEW, PAID и CANCELLED. Позже продюсер добавляет REFUNDED, а старый потребитель умеет обрабатывать только прежние значения.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1. Достаточно ли просто добавить значение UNKNOWN в собственное перечисление потребителя?

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

  1. Можно ли всегда игнорировать неизвестные значения событий?

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

  1. Почему проверка схемы на совместимость не гарантирует решение проблемы?

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