руководство по интеграцииобновлено 2026-08-20

Уведомления о спортивных событиях: очередь и независимые каналы

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

Иллюстрация к материалу: Уведомления о спортивных событиях: fan-out, outbox и независимые каналы
Elexfedi / Wikimedia CommonsCC BY-SA 4.0

Уведомление — побочный эффект, а не часть основной транзакции

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

Исходящая очередь и независимые каналы

Worker читает outbox, применяет подписки и создаёт delivery jobs. Push, email и in-app обрабатываются раздельно. Один упавший канал не удерживает очередь остальных. Дедупликационный ключ строится по domain event + user + channel.

Доставка минимум один раз без раздражающих дублей

Полностью exactly-once между нашей системой и внешним push API практически не нужен. Достаточно at-least-once внутри очереди плюс дедупликация перед отправкой и idempotent обработка delivery result, если провайдер позволяет передать внешний ключ.

Повтор с бюджетом, а не вечная очередь

Для каждого типа уведомления задаётся полезное окно. Напоминание о начале события бессмысленно доставлять через часы после старта. Job хранит expires_at; после него помечается expired и не повторяется. Ошибки токена устройства отделяются от временных ошибок провайдера.

Деградация канала не равна деградации продукта

При проблемах push интерфейс продолжает работать. Операционный статус notification subsystem влияет только на доставку. In-app канал может оставаться доступным как более контролируемый fallback, если его данные хранятся внутри платформы.

Почему очередь доставки нужно сегментировать

Одна общая очередь для push, email и других каналов выглядит проще, но делает сбой одного провайдера проблемой для всех уведомлений. Гораздо устойчивее фиксировать событие в outbox, а затем обрабатывать каналы независимо с собственными retry-политиками и лимитами.

Сегментация помогает и в эксплуатации. Если push-провайдер отстаёт, команда видит возраст именно push-сообщений и может временно ограничить менее важные темы, не затрагивая email или внутренние уведомления. При этом дедупликация должна опираться на стабильный event key, а не на текст сообщения.

  • Хранить channel delivery status отдельно от бизнес-события.
  • Ограничивать повторные попытки по времени и количеству.
  • Измерять возраст самого старого сообщения и процент просроченных доставок.

Возраст уведомления важнее длины очереди

Очередь из десяти тысяч сообщений может быть нормальной, если она разгребается за секунды. Двести уведомлений могут быть проблемой, если старейшее ждёт три минуты и речь идёт о начале матча. Поэтому наблюдаемость строится вокруг delivery age, success rate по каналу и причин окончательного drop.

Fan-out также стоит отделить от пользовательских preferences. NotificationPreferences решает, что человек хочет получать, а доставка — какой канал доступен прямо сейчас. Временный отказ push не должен блокировать in-app inbox или заставлять основную транзакцию ждать внешнего сервиса.

Для дедупликации полезен стабильный event key: повторная обработка outbox возвращает тот же notification identity. Это позволяет жить с at-least-once доставкой внутри системы без двойных уведомлений на устройстве.

Смотрим возраст, а не только размер очереди

Queue depth без контекста плохо читается: ночью тысяча jobs и в live-пик тысяча jobs — разные ситуации. Полезнее age oldest eligible job, delivery latency по каналу и доля permanent сбойs. Так видно, успевает ли система доставлять то, что ещё имеет смысл.

Схема решения: Уведомления о спортивных событиях: fan-out, outbox и независимые каналы
Диаграмма к разбору: Уведомления о спортивных событиях: распределение потока, исходящая очередь и независимые каналы.
Следующий слой

Связанные разборы по той же архитектурной цепочке

Мобильные продукты

Мобильное приложение спортивного сервиса: связь, состояние и возврат пользователя

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

Перейти к разбору →
Эксплуатация

Контракт телеметрии: события, версии и граница приватности

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

Перейти к разбору →
Надёжность

Если поставщик спортивных данных деградирует: регламент частичного отказа

Регламент частичного отказа поставщика спортивных данных: оценка состояния, политика устаревших данных, приостановка, резервирование и восстановление.

Перейти к разбору →
Букмекерская логика

Кнопка коэффициента: доступность, изменение цены и приостановка

Как проектировать кнопку коэффициента: изменение цены, приостановка, фокус, клавиатурное управление и понятные состояния после выбора пользователя.

Перейти к разбору →