Real-Time Backend
Notification Fan-out
Instagram публикует фото Kylie Jenner (398 млн подписчиков). За 30 секунд система должна поставить в очередь почти 400 миллионов уведомлений - при этом API должен ответить мгновенно. Как это работает без коллапса?
- **Twitter/X:** гибридный fan-out - push для обычных пользователей (<10k followers), pull для знаменитостей. Это позволяет обрабатывать ~500 млн твитов в день без перегрузки storage.
- **Meta (Facebook):** система Titan обрабатывает >1 млрд уведомлений в день через Kafka + Cassandra с multi-level batching. Каждый уровень стека батчит отдельно: события, DB-записи, push-токены.
- **Apple APNs:** принимает максимум 300 запросов/сек на токен. Без throttling крупные приложения мгновенно получают rate limit и теряют доставляемость для всех пользователей, не только активных.
- **SendGrid:** ограничивает новые IP-адреса до 200 писем/день при старте. Нарушение warm-up протокола ведёт к попаданию в спам-листы провайдеров - и восстановление репутации IP занимает недели.
Fanout на миллионы получателей
Когда Instagram знаменитости с 50 млн подписчиков публикует фото, система должна создать уведомления для каждого из них. Это называется **fan-out** - одно событие размножается в миллионы задач. Наивный подход - цикл в момент публикации - гарантированно убьёт сервер: 50 млн INSERT-ов занимают часы, а пользователь ждёт ответа от API.
Twitter в 2013 году имел проблему с fan-out у знаменитостей. Публикация Барака Обамы тригgerila ~8 млн уведомлений - система лагала минутами. Решение: гибридный подход - fan-out на запись для обычных пользователей, fan-out на чтение для знаменитостей (>10k фолловеров).
Ключевое решение - асинхронность. Публикация возвращает 200 OK мгновенно, а fan-out идёт в фоне через очередь сообщений (Kafka, SQS, RabbitMQ). Пользователь не ждёт, пока уведомления разойдутся по всем 50 млн подписчикам.
У пользователя 20 миллионов подписчиков. Какую стратегию fan-out выбрать для его публикаций?
Batch Processing уведомлений
Когда fan-out генерирует миллионы задач, воркеры должны обрабатывать их батчами - иначе overhead на каждый отдельный job съест всю пропускную способность. Batch processing - это группировка однотипных операций для снижения latency и стоимости.
Размер батча - это трейдоф. Слишком мал (10) - overhead на каждый вызов доминирует. Слишком велик (100k) - один отказ теряет весь батч, а транзакция держит lock слишком долго. Практика: 500-1000 для DB inserts, 100-500 для push-сервисов (лимит FCM - 500 токенов за вызов).
Meta (Facebook) обрабатывает ~1 млрд уведомлений в день. Их система Titan использует батчинг на каждом уровне: группировка событий перед записью в Cassandra, группировка push-токенов перед отправкой в APNs/FCM, группировка email-адресов перед передачей в SendGrid.
FCM (Firebase Cloud Messaging) принимает максимум 500 токенов за один multicast-вызов. Нужно отправить push 50 000 пользователям. Сколько вызовов FCM потребуется?
Priority Queue для уведомлений
Не все уведомления одинаково срочны. Push о платеже в 3 часа ночи критичен, маркетинговая рассылка - нет. Priority queue позволяет гарантировать доставку важных сообщений даже при перегрузке системы, отодвигая низкоприоритетные задачи.
Slack использует три уровня приоритета: critical (прямые сообщения), normal (уведомления канала), low (email-дайджесты). При инциденте системы рассылки, Slack намеренно дропает P2/P3 уведомления, чтобы гарантировать доставку P0/P1. Пользователи замечают задержку дайджестов, но не теряют важных алертов.
На практике приоритетные очереди реализуют либо через отдельные физические очереди (разные Kafka topics, разные SQS queues), либо через score в Redis Sorted Set - где score это `priority * 1e13 - timestamp`, что гарантирует обработку по приоритету, а внутри приоритета - по времени поступления.
Система перегружена. Воркеры не справляются с потоком уведомлений. Какое уведомление нужно обработать первым?
Throttling уведомлений
Throttling - намеренное ограничение скорости отправки уведомлений. Это не баг, а фича: защита от спама, соблюдение лимитов провайдеров (APNs, FCM, SendGrid), и - главное - уважение к пользователю. Получить 50 пушей за секунду означает удалить приложение.
SendGrid throttlит отправителей автоматически: если IP шлёт >1000 писем/мин без warm-up, письма идут в спам или блокируются. Правильная раскрутка IP - начать с 200 писем/день, удваивать каждые 2 дня. Amazon SES имеет дефолтный лимит 14 писем/сек - его можно увеличить через support request.
Throttling - это потеря данных. Если уведомление не отправлено сразу, оно потеряно.
Throttling - это управление потоком. Задержанные или агрегированные уведомления не теряются - они либо откладываются до следующего окна, либо сворачиваются в одно сообщение с суммарным счётчиком.
Пользовательский опыт важнее мгновенности. 200 отдельных пушей хуже, чем одно '200 лайков на пост'. Throttling - это про качество доставки, не про потерю.
Пользователь за час получил 200 лайков на пост. Как правильно throttle уведомления?
Итоги
- **Fan-out стратегия зависит от аудитории**: push (на запись) для обычных пользователей, pull (на чтение) для знаменитостей с миллионами подписчиков - гибридный подход используют Instagram и Twitter.
- **Батчинг снижает нагрузку на порядки**: 50 000 push-уведомлений = 100 вызовов FCM по 500 токенов вместо 50 000 отдельных HTTP-запросов.
- **Throttling - это забота о пользователе**: агрегация '200 лайков' лучше 200 отдельных пушей, а rate limiting защищает репутацию IP и предотвращает блокировки на стороне провайдеров.
Связанные темы
Fan-out уведомлений пересекается с несколькими фундаментальными паттернами distributed systems:
- Message Queues (Kafka, SQS) — Основа асинхронного fan-out - без очередей публикация блокировалась бы до завершения всех уведомлений
- Rate Limiting — Throttling уведомлений - специализированный случай rate limiting, применяемый как к входящему потоку событий, так и к исходящим вызовам провайдеров
- Activity Feeds — Fan-out уведомлений и fan-out для activity feed решают похожую задачу разными методами - следующий урок
Вопросы для размышления
- Приложение внезапно вирусно выросло: один пост набрал 10 млн лайков за час. Какие части системы уведомлений сломаются первыми и почему?
- Как определить правильный размер батча для bulk INSERT в PostgreSQL? Какие факторы влияют на оптимальное значение?
- Пользователь жалуется: 'Я получаю уведомления о лайках через 2 часа'. Что могло пойти не так в системе приоритизации очередей?