TGViewer
About Python [ru] About Python [ru] @python_tesst · 6.44K subscribers
Post #2763 447
⁣Профилирование async-генераторов: GC-latency, HWM и паттерны утечки корутин в high‑load FastAPI‑сервисах

В продакшене async-генераторы часто воспринимаются как идеальный механизм для стриминга больших данных. Но при анализе p99 latency я обнаружил, что основной источник задержек — не медленные запросы к БД, а скрытые проблемы с утечками корутин и сборкой мусора.

Проблема 1: GC-latency при разрыве соединения

Когда клиент прерывает соединение, async-генератор продолжает висеть с циклическими ссылками. Поколенческий сборщик мусора начинает аварийные сборки, и latency может улетать за секунду.

async def stream_data():
for i in range(10**6):
yield await fetch_chunk(i)


Утечка: клиент ушёл, но генератор не завершён. Решение — finally с aclose() или обёртка через @contextlib.asynccontextmanager. Правило: если ты не контролируешь время жизни генератора, контролируй очистку.

Проблема 2: HWM (high water mark) и резервирование стека

Каждый async-генератор резервирует стек корутины при создании. В проде с тысячами одновременных запросов это даёт ощутимый overhead. Для профилирования используйте gc.get_objects() с фильтром на AsyncGeneratorType:

import gc, types
from collections import Counter

gen_count = Counter()
for obj in gc.get_objects():
if isinstance(obj, types.AsyncGeneratorType):
gen_count[type(obj).__name__] += 1


Рост счётчика — явный признак утечки. HWM можно оценить через sys.getsizeof(), но лучше фокусироваться на количестве живых генераторов.

FastAPI-specific паттерны утечек

На ревью часто вижу три типичные ошибки:
- Тайм-ауты: FastAPI отменяет задачу, но aclose() не вызывается.
- Циклические ссылки: генератор держит request-объект, GC в тупике.
- SSE-генераторы, висящие вечно, если клиент не закрыл соединение.

В продакшене включаю PYTHONASYNCIODEBUG=1 для ловли Task was destroyed but it is pending. В тестовых средах — gc.set_debug(gc.DEBUG_LEAK). Для стриминга FastAPI использую шаблон с aclosing:

from contextlib import aclosing

async def safe_stream():
async with aclosing(async_generator()):
async for item in async_generator():
yield item


Практический совет: всегда оборачивайте async-генераторы в контекстный менеджер с гарантированным вызовом aclose(). Это снижает p99 latency на сотни миллисекунд.

Вывод:
Один забытый async-генератор в high-load FastAPI-сервисе способен превратить стриминг в источник неконтролируемых задержек, поэтому профилирование GC и утечек корутин — обязательный шаг при оптимизации latency.
More from @python_tesst
  1. Sep 29, 2026Claude Sonnet 5.5 — уже не слив, релиз официально состоялся Прайс не трогали: $2 за миллио…
  2. Sep 29, 2026Post #3194
  3. Sep 29, 2026Дженсен Хуанг выкатил NVIDIA Open Agent Safety Platform — и притащил с собой 100+ партнеро…
  4. Sep 29, 2026Стикмену теперь под силу снести любой сайт. Чувак под это навайбкодил целую игруху: грузиш…
  5. Sep 27, 2026OpenAI метит в подписку за 500 баксов Что там в описании тарифа? Пока что от ChatGPT Pro о…
  6. Sep 27, 2026Свежая обложка The Economist подъехала Журналисты: да мы вообще не сгущаем краски Те же жу…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →