Асинхронный краш, который молча убивал процесс

В проекте Bot Social Publisher я столкнулся с багом, который преподал мне урок о том, как Python молча убивает асинхронные задачи.
Всё началось с того, что процесс падал каждые 8–10 минут. Exit code 0, как будто ничего не произошло. PM2 думал, что это нормальный цикл, и перезапускал всё заново. В логах структурного ничего не было — просто обрыв на середине слова.
Я начал добавлять debug-маркеры в критические точки: перед extract, после linking, перед formation. Маркеры появлялись до определённого момента, а потом — тишина. Это означало только одно: краш происходит в асинхронном task’е, который создан через asyncio.create_task(), но не добавлен в основное asyncio.gather().
Нашёл проблему в _extract_facts_pipeline — это была задача, создаваемая внутри crawl_once(). Если в ней поднимался exception, он просто испарялся. Основной event loop об этом не знал, потому что task не был часть собранной группы. Python молча падает в таких случаях — exception в orphaned task’е не выводится в stderr.
Решение было простым, но требовало переделки архитектуры: все критические задачи теперь либо ловят exception вручную и логируют его, либо зарегистрированы в основном gather(). Вместо:
asyncio.create_task(self._extract_facts_pipeline())
Перешёл на явный контроль — task регистрируется и отслеживается.
Дальше обнаружилась конкурентная проблема: _extract_facts_pipeline и translation loop одновременно пытались использовать один инстанс Ollama на одном порту. Dual-port routing, который я писал, не работал как ожидалось. Переделал маршрутизацию — теперь потребители разнесены по портам явно, через конфиг.
После этого цикл прошёл 5+ минут без крахов. Рестартов всё ещё было 450 в логе, но это уже были контролируемые перезагрузки, а не молчаливые падения.
Вывод прост: асинхронная архитектура требует такого же внимания к обработке ошибок, как синхронный код, но Python здесь хитрее. Orphaned task’и падают молча, и если ты не проверяешь логи на предмет неполных маркеров, потратишь дни на отладку фантомного бага.
TypeScript в этом плане честнее — там ты не можешь просто так создать task и забыть про него, система будет ругаться. 😄
Метаданные
- Session ID:
- grouped_C--projects-bot-social-publisher_20260418_1955
- Branch:
- main
- Dev Joke
- TypeScript — единственная технология, где «это работает» считается документацией.