Привет, Хабр!
Меня зовут Сергей Бакалдин, я работаю в команде ComPath. Сегодня я расскажу историю о том, как одна неосторожная e‑mail-рассылка чуть не положила фронтенд-систему «Спортмастера», и как мы от этого открестились с помощью гибкого механизма управления скоростью рассылок.
Предпосылки для создания механизма

Итак, главная цель компании «Спортмастер» – это продажа спортивных товаров. Соответственно, для компании критически важно, чтобы пользователи могли в любое время зайти на сайт или в мобильное приложение, выбрать интересующий их товар и купить. Однако у любой системы есть свои ограничения, и наша фронт-система (сайт, мобильное приложение) здесь не исключение: она может обрабатывать ограниченное число пользователей в единицу времени.
При нормальной нагрузке всё ок. Но резкий всплеск приводит к:
длительным откликам;
ошибкам 5хх;
полнейшему отказу – падению системы.
Последствия превышения допустимой нагрузки для нас очень критичны:
компания несет прямой убыток (покупатель не может совершить заказ);
ущерб доверия – клиент уходит к конкуренту.
Итак, перед нами встает следующая задача: запускать рассылки так, чтобы не перегрузить фронт, но при этом сохранить привлеченный трафик.
Поиск баланса
В процессе рассылок участвуют следующие компоненты:
Campaign – инициирует акцию, предоставляет сегмент и контент;
ComPath – применяет логику отправки, агрегатор рассылок;
IPS (Incident Prevention System) – мониторит фронты и отправляет тревоги при перегрузке;
Основные каналы коммуникации: e-mail (~80‑90 Кб), пуши, СМС, Viber. Наиболее энергозатратные из них – e-mail.
Большое количество пользователей ведет к повышению нагрузки на фронты. Источником этих пользователей является ComPath, который занимается рассылкой маркетинговых коммуникаций, получаемых от системы Campaign.

Самыми проблемными коммуникациями для нас являются письма, т.к. они требуют подготовки (ComPath формирует письма по шаблонам) и большие по размеру (среднестатистическое письмо весит порядка 80-90 Кб); при этом коммуникации должны рассылаться быстро.
Исходя из всех вводных, от FAST-команды нам пришла следующая функциональная схема по управлению скоростью: IPS имеет на руках все метрики по поведению фронтов и сообщает заинтересованным системам о превышении нагрузки на одном или нескольких фронтах. Campaign – одна из таких систем. Она принимает от IPS информацию о состоянии фронта, дополняет её данными о рассылках, влияющих на проблемный фронт, и передает в ComPath, который применяет всю логику и управляет скоростью рассылок.

Помимо функциональной схемы, FAST выдвинул ещё несколько требований:
возможность автоматического управления скоростью – основная ручка контроля скорости; прием API-запросов от Campaign полностью реализует это требование;
ручное управление скоростью рассылок по акциям – это специальная ручка в первую очередь для ГЭСМиК (Группы Эксплуатации Систем Маркетинга и Коммуникаций), они могут ей пользоваться в трех случаях:
повышение приоритета по каким-то конкретным акциям: если есть понимание, что среди одновременно запущенных рассылок одна наиболее важная, скорость увеличивается вручную, тем самым повышая её приоритет;
остановка устаревших/ненужных рассылок. Например, сама рассылка была призвана привлечь клиентов на стрим. Стрим закончился, а рассылка ещё нет. Можно воспользоваться ручным управлением и остановить ее полностью;
замедление скорости при проблемах с фронтом. Например, ІРЅ еще не успел среагировать или же запрос от ІРЅ потерялся и не дошел до ComPath;
3) управление всей кампанией (бизнес-акцией) сендерами. Опять же, это ручка для ГЭСМиК, она может использоваться для остановки всех рассылок конкретного бизнеса, если фронт не справляется, или наоборот для возобновления всех рассылок и дальнейшего продолжения работы в обычном режиме;
4) аудит изменения скорости: каждое изменение скорости, вызванное обращением по API, должно быть записано в БД (Базу данных) – эта информация должна помочь в расследовании инцидентов;
5) независимая обработка рассылок по акциям: каждая акция по-разному влияет на фронты и замедляться должны только те акции, которые ведут пользователей на проблемный фронт;
6) учет временных окон: для коммуникаций могут быть установлены временные рамки по отправке коммуникаций, которые ComPath должен учитывать при отправке.
Архитектуры рассылки: почему не все работают
Были разработаны и опробованы три варианта архитектуры. Для выбора лучшего из них мы свели все данные в таблицу:

Решение было принято в пользу варианта №3 – топик на каждую акцию с контролем скорости через speed-limiter + Redis + библиотеку для ограничения скорости bucket4j.

Что пошло не так, или как мы дорабатывали систему в моменте
При первичном тестировании данной схемы мы не выявили каких-то больших проблем; казалось, что всё работает нормально, но эксплуатация в проде быстро всё расставила на свои места: буквально на второй день мы положили Kafka. Причиной тому было большое количество акций, полученных от Campaign.
Эта проблема подсветила недоработку на этапе аналитики. Мы ожидали, что акций, в которых требуется замедление, будет до 10 штук, а фактически к нам прилетело в разы больше (не десять, а десятки). Причем некоторые акции были «пустышками»: пара писем, для которых не было смысла держать топик.

Дальше – больше: практически каждый день прилетали какие-то разные инциденты. Причина крылась в плохой синхронизации между экземплярами speed limiter, что приводило к рассинхрону скоростей и авариям.

Проанализировав ошибки, мы пришли к следующему:
ограничили число топиков через явный лимит партиций, договорились с бизнесом, что согласовывать будем только нужные акции;
-
разнесли бизнес-логику: topic advisor (один экземпляр) рассчитывает и дает рекомендации, speed limiter (несколько экземпляров) слушает topic advisor, ограничивает скорость рассылок и обеспечивает соблюдение ограничений временных окон.
В результате появился вариант архитектуры №4, в котором мы сохранили независимость по акциям, избежали рассинхронов при изменениях скорости, сохранили масштабируемость и стабильность Kafka в целом.
Выводы и рекомендации для коллег
1. Защита от неожиданностей. Ограничьте число акций по партициям заранее – это спасло бы нас. Сами мы ограничение реализовали уже после падения Kafka.
2. Не бойтесь пересматривать архитектуру. Это не «фиаско», а жизненно важный аналитический шаг.
3. Разграничивайте расчёт и применение скорости – разнесение ответственности помогает синхронизации и отказоустойчивости.
4. Используйте аудит-логи. В лог записывается каждое изменение скорости, и эти данные могут использоваться для отслеживания и анализа инцидентов, связанных с работой системы.
5. Маленькие акции (1–2 письма) не стоят отдельных топиков ни по нагрузке, ни по бюджету.
Заключение
Иногда кажется: «Да чего там, всего лишь одна рассылка». Но, столкнувшись с падением фронта, понимаешь – это не мелочь.
Через массовые тикеты и изоляцию Kafka мы прошли путь от хрупкой, требующей постоянного внимания разработчика, системы к стабильному и управляемому механизму.
P.S.: После всех доработок мы успешно пережили «Черную пятницу» – разослали более полумиллиарда маркетинговых пушей и писем клиентам, не уронив при этом ни одного фронта.
VenbergV
«Ах Вы дрянь этакая! Занимаетесь грязным делом, да ещё и признаётесь? Да как Вам не стыдно! » © — "О бедном гусаре замолвите слово."