Почему нельзя просто синхронно вызвать сервис уведомлений
Что проверяют
Понимаете ли вы, что уведомление это побочный эффект, который не должен решать судьбу основной операции, и отличаете ли асинхронный вызов внутри процесса от выноса работы в отдельный обработчик. Второе важнее: на asyncio кандидаты отвечают часто, а про надёжность доставки почти никогда.
Как отвечать
Синхронный вызов связывает доступность своего сервиса с доступностью чужого. Latency складывается, отказ почтового шлюза роняет оформление заказа, ретраи внутри обработчика держат воркер занятым, а под нагрузкой пул соединений заканчивается целиком.
Правильная схема: зафиксировать намерение и вернуть ответ, доставку сделать отдельно. Тут важно не остановиться на await. Корутина или BackgroundTasks живут в том же процессе, и при рестарте, деплое или падении по памяти задача исчезает бесследно.
Надёжный вариант это очередь (Celery, ARQ, RQ поверх Redis, либо Kafka или RabbitMQ) плюс паттерн transactional outbox: в одной транзакции с бизнес-данными пишем строку в таблицу исходящих, отдельный воркер её забирает и отправляет. Доставка получается at-least-once, поэтому нужен ключ идемпотентности и дедупликация на стороне отправки, повторы с растущей паузой и очередь необработанного.
Хорошо назвать и обратную сторону: появляется лаг, отладка усложняется, нужен мониторинг длины очереди и возраста самого старого сообщения.
Где спотыкаются
Называют asyncio.create_task или BackgroundTasks фоновой отправкой и считают вопрос закрытым. Это тот же процесс и та же судьба: рестарт контейнера, и уведомления нет, а в логах пусто, потому что исключение внутри незавершённой задачи никто не прочитал.
Ставят блокирующий вызов внутрь async-обработчика. requests.post останавливает не одну корутину, а весь событийный цикл, и сервис перестаёт отвечать всем клиентам. Нужен httpx.AsyncClient или вынос в пул через run_in_executor.
Отправляют до коммита. Письмо про созданный заказ уходит, транзакция откатывается, и пользователь получает подтверждение того, чего в базе нет. Запись в outbox в той же транзакции снимает именно эту проблему.