Стадия 1 — устранение зависаний: таймауты на все TG RPC + починка фонового воркера #1

Closed
opened 2026-07-05 04:01:07 +03:00 by agent_vscode · 0 comments
Collaborator

Цель

Убрать главный источник полных зависаний приложения: Telegram RPC без таймаутов, удерживающие глобальный throttle-гейт (tg_throttle, concurrency=1), и молчаливую смерть фонового воркера загрузок.

Полный план: plans/2026-07-05-stability-fix-plan.md

Контекст (что чиним)

  • Один зависший get_chat_history/get_chat под tg_rpc() держит единственный пермит навсегда → все RSS/HTML-запросы виснут (tg_cache.py:142-144, 216-217).
  • background_download_worker: task_done() в finally вызывается даже когда get() не вернул элемент → ValueError → воркер умирает молча (api_server.py:620-637). Дальше очередь (maxsize=100) забивается, await download_queue.put() блокируется навсегда (except QueueFull — мёртвый код: await put() его не бросает) → встаёт и уборка кэша.

Задачи

  • Config: добавить tg_rpc_timeout (env TG_RPC_TIMEOUT, default 60)
  • cached_get_chat_history: собрать итерацию в корутину, обернуть в asyncio.wait_for внутри async with tg_rpc()
  • cached_get_chat: wait_for вокруг client.get_chat
  • _reply_enrichment (rss_generator.py:505): добавить tg_rpc() + wait_for
  • PostParser.get_post (post_parser.py:98): wait_for(30); удалить print() (строка 95)
  • /health (api_server.py:906): wait_for(get_me(), 10)
  • Воркер: task_done() только после успешного get(); FloodWait ловить до Exception и спать min(e.value+5, 900)
  • download_new_files: await put()put_nowait() (оживить except QueueFull)
  • Supervision: done-callback на background_task/worker_task в lifespan — CRITICAL-лог + перезапуск (rate-limit 1/60с)
  • Тесты: зависший мок-RPC (таймаут у первого, второй проходит, пермит не утёк); воркер с вечно падающим download жив, queue.join() завершается

Где не наебаться

  • wait_for — вокруг RPC, не вокруг _sem.acquire(): очередь на гейте — легитимный backpressure, иначе ложные таймауты при штатной пачке фидов.
  • async for не заворачивается в wait_for напрямую — только через промежуточную корутину.
  • CancelledError не глотать (except Exception её и так не ловит в 3.11 — не «улучшать» до BaseException).

DoD

Ни один путь кода не ждёт Telegram без таймаута; воркер не умирает молча; pytest зелёный (174 baseline + новые).

Зависимости: нет. Деплоится первой.

## Цель Убрать главный источник полных зависаний приложения: Telegram RPC без таймаутов, удерживающие глобальный throttle-гейт (`tg_throttle`, concurrency=1), и молчаливую смерть фонового воркера загрузок. Полный план: [plans/2026-07-05-stability-fix-plan.md](https://gitea.vvzvlad.xyz/vvzvlad/pyrogram-bridge/src/branch/fix/stability/plans/2026-07-05-stability-fix-plan.md) ## Контекст (что чиним) - Один зависший `get_chat_history`/`get_chat` под `tg_rpc()` держит единственный пермит навсегда → **все** RSS/HTML-запросы виснут (tg_cache.py:142-144, 216-217). - `background_download_worker`: `task_done()` в `finally` вызывается даже когда `get()` не вернул элемент → `ValueError` → воркер умирает молча (api_server.py:620-637). Дальше очередь (maxsize=100) забивается, `await download_queue.put()` блокируется навсегда (`except QueueFull` — мёртвый код: `await put()` его не бросает) → встаёт и уборка кэша. ## Задачи - [ ] Config: добавить `tg_rpc_timeout` (env `TG_RPC_TIMEOUT`, default 60) - [ ] `cached_get_chat_history`: собрать итерацию в корутину, обернуть в `asyncio.wait_for` **внутри** `async with tg_rpc()` - [ ] `cached_get_chat`: `wait_for` вокруг `client.get_chat` - [ ] `_reply_enrichment` (rss_generator.py:505): добавить `tg_rpc()` + `wait_for` - [ ] `PostParser.get_post` (post_parser.py:98): `wait_for(30)`; удалить `print()` (строка 95) - [ ] `/health` (api_server.py:906): `wait_for(get_me(), 10)` - [ ] Воркер: `task_done()` только после успешного `get()`; `FloodWait` ловить до `Exception` и спать `min(e.value+5, 900)` - [ ] `download_new_files`: `await put()` → `put_nowait()` (оживить `except QueueFull`) - [ ] Supervision: done-callback на `background_task`/`worker_task` в lifespan — CRITICAL-лог + перезапуск (rate-limit 1/60с) - [ ] Тесты: зависший мок-RPC (таймаут у первого, второй проходит, пермит не утёк); воркер с вечно падающим download жив, `queue.join()` завершается ## Где не наебаться - `wait_for` — вокруг **RPC**, не вокруг `_sem.acquire()`: очередь на гейте — легитимный backpressure, иначе ложные таймауты при штатной пачке фидов. - `async for` не заворачивается в `wait_for` напрямую — только через промежуточную корутину. - `CancelledError` не глотать (`except Exception` её и так не ловит в 3.11 — не «улучшать» до `BaseException`). ## DoD Ни один путь кода не ждёт Telegram без таймаута; воркер не умирает молча; pytest зелёный (174 baseline + новые). **Зависимости**: нет. Деплоится первой.
Sign in to join this conversation.