Как структурировать уведомления
Что проверяют
Умеете ли вы спроектировать отправку уведомлений так, чтобы сообщение не терялось, не уходило дважды и не тормозило основной запрос. Следом обычно спрашивают, что будет, если SMS-провайдер лёг на час, и как не заспамить пользователя.
Как отвечать
Разбейте систему на слои:
- событие: бизнес-код не шлёт письма сам, а публикует событие «заказ оплачен». Чтобы событие не потерялось между коммитом и публикацией, используют transactional outbox: запись в таблицу outbox в той же транзакции, отдельный процесс переносит её в брокер;
- сервис уведомлений: решает, кому, по каким каналам и каким шаблоном. Здесь же настройки пользователя: отписки, тихие часы, язык. Шаблоны удобно держать в Jinja2;
- очередь: Celery с RabbitMQ или Redis, либо Kafka, если событий много и потребителей несколько. Коды подтверждения идут отдельной очередью, чтобы не стоять за рассылкой;
- каналы: адаптеры с общим интерфейсом через
typing.Protocolилиabc.ABC: email, SMS, push через FCM и APNs, мессенджеры. Новый канал это новый класс; - надёжность: повторы с экспоненциальной задержкой (в Celery
autoretry_forиretry_backoff), ключ идемпотентности на каждое уведомление, dead letter очередь для того, что так и не ушло; - учёт: таблица со статусами
pending,sent,failed,delivered, чтобы отвечать поддержке и считать доставляемость.
Отдельно упомяните лимиты провайдеров и группировку: десять лайков за минуту лучше отправить одним уведомлением.
Где спотыкаются
Отправляют письмо прямо в обработчике HTTP-запроса. Медленный SMTP держит запрос, а ошибка провайдера роняет всю операцию.
Ставят повторы без идемпотентности. После таймаута задача перезапускается, и пользователь получает три одинаковые SMS.
Кладут в одну очередь коды входа и массовую рассылку. В день рассылки код приходит через двадцать минут, когда он уже никому не нужен.