Новая функцияtrend-analisis
Молчаливый краш каждые восемь минут: как мы искали баг в конвейере тренд-анализа
Работаю над **Trend Analysis** — системой, которая вытаскивает из кластеров событий настоящие тренды. Идея простая: тренд — это не один факт, а паттерн, видимый сразу в нескольких независимых источниках. Например, "AI funding accelerating" подтверждается инвестициями OpenAI, Anthropic и Mistral одновременно.
Добавили в систему извлечение `domain_tags` — метаданные, которые помогают понять, в каких сферах появляются тренды. Написал миграцию базы данных (092), обновил Pydantic-модель `ExtractionResult`, задеплоил в production. Всё выглядело хорошо.
Потом начался ад.
Pipeline рестартовался сам по себе каждые 8–10 минут. Не crashing с ошибкой, не падая с исключением — просто выходил нормально (exit code 0), будто завершил работу. PM2 считал это штатным поведением, счётчик restarts поднялся до 450. Логи не показывали nothing — ни ошибок, ни предупреждений, ни exception'ов.
Я начал добавлять debug-маркеры на критических этапах. "PHASE_DEBUG" перед главной стадией extraction. Ждал цикла за циклом. Маркер никогда не появлялся.
Потом заметил: логи говорят "Fact extraction done", потом сразу — крах. Между фазой extraction и следующей стадией что-то умирало молча. Проверил `_propagate_domain_tags` — новый код, который я добавил в event_linker. Он вызывается после commit. Обёрнут в try/except. Не должно быть проблем.
Но потом я посмотрел на главный `asyncio.gather()` в функции `main()`. Там пять задач: `_crawl_start_with_flag`, `_retry_loop`, `_phase2_loop`, `_convergence_loop`, `_wal_checkpoint_loop`. И `gather()` **без флага `return_exceptions=True`**. Это значит, если ЛЮБАЯ из них упадёт — весь gather упадёт, и процесс завершится.
Но логов нет...
А потом вспомнил: я использую `asyncio.create_task()` для запуска `_extract_facts_pipeline` ВНУТРИ `crawl_once()`. Это отдельная задача, не добавленная в основной gather. Если она поднимает exception — в Python 3.13 это просто логируется где-то в недрах event loop, но не убивает процесс явно. Процесс выходит чисто, потому что задача закончилась (с ошибкой).
Решение было банальным: либо добавить эту задачу в основной gather, либо завернуть её в try/except с явным логированием. Я выбрал второе — явное логирование всех ошибок внутри `_extract_facts_pipeline`.
После fix pipeline работал стабильно. Uptime перевалил за 30 минут. Никаких рестартов.
**Урок:** когда Python молчит, ищи asyncio. Необработанные исключения в create_task() — это коварный враг, потому что он не скалывается, он просто завершает процесс как ни в чём не бывало. 😄
#claude#ai#python#javascript#git#api#security
18 апр. 2026 г.