Стадия 4 — гигиена event loop: рендер фидов в поток, lazy raw_message, один sanitize на выход #4
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Цель
Event loop не должен выполнять CPU-работу > ~50 мс: во время генерации фида сейчас замирает всё приложение, включая стриминг медиа.
Полный план: plans/2026-07-05-stability-fix-plan.md
Контекст (что чиним)
copy.deepcopyсотен Message-объектов на лупе (rss_generator.py:38, активен приTIME_BASED_MERGE=True— включено в проде).process_messageсинхронно на лупе для каждого сообщения (rss_generator.py:211-230), внутри — 4+ вызова bleach на сообщение (post_parser.py:672, 752, 833, 920; есть диагностикаdiag_sanitize_slow>50мс/вызов) иstr(message)— полная сериализация каждого поста (post_parser.py:523).Задачи (строгий порядок: 4.2 → 4.3!)
process_message(..., include_raw: bool = False):str(message)только для JSON-выдачи и debug-HTML, в фидах — никогдаprocess_message:_save_media_file_ids(post_parser.py:977-1028) больше не делаетget_running_loop()/create_task, а копит(channel, post_id, fid, ts)вself._pending_media_ids; после рендера вызывающий код один раз делаетawait asyncio.to_thread(upsert_media_file_ids_bulk_sync, ...)— новая функция в file_io.py сexecutemany. Счётчик_persist_pending_countудалить_create_time_based_media_groups/_create_messages_groups/_trim_messages_groups/_render_messages_groups— внутри нет ни одного await → превратить в sync-функции и выполнять однимto_threadизgenerate_channel_rss/generate_channel_html(deepcopy уезжает туда же)/html/{ch}/{id}— добавить единственный проход в_format_html;/json— один проход по body+footer вprocess_messageraw_messageв<pre>прогнать черезhtml.escape<script>,onerror=,javascript:→ вычищено во всех трёх выводах); media id сохраняются после рендера (bulk upsert); deepcopy Message из pickle-кэша не падает в потокеГде не наебаться
asyncio.get_running_loop()внутри потока бросит RuntimeError, ветка тихо упадёт в except-лог, записи media id молча перестанут сохраняться и фоновый прогрев кэша умрёт. Строго 4.2 до 4.3.create_task, ниget_running_loop, ни asyncio-примитивов.DoD
Во время генерации фида на 100 сообщений параллельный
/ping(стадия 6) отвечает < 100 мс; XSS-тесты зелёные; media id сохраняются.Зависимости: нет жёстких;
/pingдля DoD-проверки появляется в стадии 6 (можно проверить любым лёгким эндпоинтом).