$ sudo teach|
    $ sudo teach|
    IT school
  • Telegram
  • Партнёрам
  • Все курсы
$ sudo teach IT
OOO "SALEPROFIT"Контакты и реквизитыIT-Park Logo

Школа

  • Блог
  • Проверить сертификат

Сотрудничество

  • Стать учителем
  • Партнёрская программа
  • О проекте

Право

  • Оферта
  • Политика конфиденциальности

© 2023–2026 $ sudo teach IT™. All Rights Reserved. Public user contributions licensed under CC BY-SA 4.0 license with attribution required
TelegramGitHubYouTube
ГлавнаяБлогКак отлаживать проблемы с производительностью FastAPI: профилирование, трассы и поиск медленных мест по слоям

Как отлаживать проблемы с производительностью FastAPI: профилирование, трассы и поиск медленных мест по слоям

$ sudo teach IT
·8 августа 2026 г.·12 мин·67
Как отлаживать проблемы с производительностью FastAPI: профилирование, трассы и поиск медленных мест по слоям

Покажем подход «сверху вниз»: где смотреть (CPU/IO/БД/серилизация), как использовать трассировки и профили, как отличить медленную функцию от медленного ожидания. В конце — чек-лист диагностики на боевых метриках.

Содержание
1) С чего начать: определить тип проблемы (CPU vs ожидание)1.1. Быстрый диагностический разрез по метрикам2) «Сверху вниз»: карта слоёв времени запроса в FastAPI2.1. HTTP/ASGI слой и обработка запроса2.2. Бизнес-слой (CPU или логика)2.3. Слой I/O: БД и внешние сервисы2.4. Сериализация и формирование ответа2.5. Где обычно прячется «медленное»3) Профилирование: что делать, когда неясно, где тормозит3.1. CPU-профиль в async-сервисе: осторожно3.2. Явные таймеры по шагам (low friction, но очень полезно)3.3. Инструментирование ожиданий: важный трюк4) Трассировки (tracing): как увидеть “где именно” теряется время4.1. Почему tracing критичен именно для FastAPI4.2. Базовая схема: spans и кореляция4.3. Как выглядит расследование по трассам4.4. Неочевидные ловушки трассировок5) Отличить медленную функцию от медленного ожидания: практические правила5.1. Правило №1: медленное ожидание почти всегда “await внутри трассы”5.2. Правило №2: медленная функция тянет CPU и обычно видна в CPU profile5.3. Правило №3: если async блокируется синхронным кодом, это выглядит как “ожидание”, но является CPU-bound6) Поиск медленных мест по слоям: методика расследования6.1. Шаг 1: определите, какие запросы деградируют и есть ли корреляции6.2. Шаг 2: сравните CPU и I/O6.3. Шаг 3: откройте трассу “типичного” p95 и “самого плохого” p996.4. Шаг 4: на уровне БД — что искать6.5. Шаг 5: на уровне сериализации/валидации — что искать7) Метрики и дизайн эксперимента: чтобы не лечить симптом8) Чек-лист диагностики на боевых метриках8.1. Сбор фактов8.2. Разделение “CPU-bound vs I/O-bound”8.3. Трассы: поиск “длинной ветки”8.4. БД8.5. Серилизация и Pydantic8.6. Асинхронная корректность8.7. Итог: план устранения и верификацияВывод

Проблемы производительности в FastAPI редко бывают «одной ошибкой». Чаще это сочетание факторов: CPU у сервера перегревается сериализацией, I/O ждёт внешние сервисы, база «держит» транзакции, а в коде между ними лежат участки, которые медленно растут со временем (кеши без ограничений, N+1-запросы, лишние конверсии данных, неаккуратные зависимости FastAPI).

В этой статье — подход «сверху вниз»: от системных признаков (CPU/IO/БД/серилизация) к точным виновникам с помощью профилирования и трассировок. Главная цель — научиться отличать медленную функцию от медленного ожидания и собрать чек-лист диагностики на боевых метриках, который реально помогает на проде, а не только в локальной среде.


1) С чего начать: определить тип проблемы (CPU vs ожидание)

Прежде чем включать профилировщики, важно понять, что именно «болит». FastAPI запускается как ASGI-приложение, и тайминг запроса складывается из нескольких частей:

  1. CPU-работа внутри процесса: обработка логики, валидация Pydantic, формирование ответа, сериализация JSON, маппинги, генерация файлов и т.п.
  2. I/O-ожидание: сеть до внешних сервисов, чтение/запись в БД, ожидание очередей, задержки в DNS/TLS.
  3. Очереди и backpressure: рост времени ожидания в event loop, переполнения, долгие запросы, которые держат ресурсы.

1.1. Быстрый диагностический разрез по метрикам

Если у вас есть привычные метрики (Prometheus/Grafana, APM), полезно смотреть хотя бы:

  • event loop latency (если есть) или косвенно: рост времени ответа при стабильном CPU.
  • CPU usage процесса/воркера:
    • высокое CPU при растущих p95/p99 — подозрение на сериализацию/валидацию/бизнес-логику;
    • низкое/умеренное CPU при высоких p95/p99 — чаще ожидание I/O (БД/внешние сервисы).
  • количество активных запросов (in-flight) и очередь в воркерах (если используете gunicorn/uvicorn с несколькими воркерами).
  • время запросов на уровне маршрутов (middleware/endpoint metrics).
  • свои метрики по БД: время запроса, число запросов, ошибки/таймауты.
  • ошибки таймаутов и ретраи (внешние сервисы).

Профессиональная привычка: сначала разделить проблему на «CPU-bound» и «I/O-bound». Это резко сокращает пространство поиска.


2) «Сверху вниз»: карта слоёв времени запроса в FastAPI

При отладке производительности удобно мысленно разделять запрос на слои. Для FastAPI условная диаграмма такая:

2.1. HTTP/ASGI слой и обработка запроса

  • Принятие запроса, parsing заголовков.
  • Вызовы middleware (включая CORS, auth, логирование, rate limit).
  • Валидация входных данных: request body -> Pydantic модели.

2.2. Бизнес-слой (CPU или логика)

  • Основные вычисления.
  • Построение структур ответа.
  • Вызовы сервисов (снаружи — обычно I/O, внутри — CPU).

2.3. Слой I/O: БД и внешние сервисы

  • Асинхронные запросы к БД (например, SQLAlchemy Async / asyncpg).
  • HTTP-вызовы к другим API (например, aiohttp/httpx).
  • Файловые операции, кеши (Redis).

2.4. Сериализация и формирование ответа

  • response_model и Pydantic сериализация.
  • JSON генерация.
  • Превращение datetime/decimal в строки/числа.
  • Потенциально — сжатие (gzip/br) на уровне middleware или reverse proxy.

2.5. Где обычно прячется «медленное»

Частые причины по слоям:

  • Валидация/серилизация: большие схемы Pydantic, вложенные модели, много полей, сложные валидаторы, json_encoders, @root_validator.
  • БД: N+1 запросы, отсутствие индексов, лишние сортировки, долгие транзакции, несоответствие типов в WHERE, неверные join-и.
  • Внешние сервисы: таймауты, медленные ретраи, отсутствие дедлайнов, большие payload, отсутствие HTTP keep-alive.
  • Код в event loop: синхронные блокировки (использование requests/файловых чтений без executor), тяжёлые вычисления в async-хендлере.
  • Механика FastAPI/Starlette: лишние middleware, слишком частое логирование с форматированием “на горячую” (особенно синхронные хуки), сериализация огромных ответов.

3) Профилирование: что делать, когда неясно, где тормозит

Профилирование в Python обычно делят на:

  • статистическое CPU-профилирование (sample-based);
  • трассировки по времени (tracing/spans);
  • инструментирование по участкам (manual timers, contextvars, metrics).

Для FastAPI важно: CPU профилируют во времени исполнения в процессе, а трассы — чтобы связать запрос с БД/внешними сервисами и увидеть “ожидание”.

3.1. CPU-профиль в async-сервисе: осторожно

Классический инструмент — py-spy (часто удобнее, чем “встраиваемое” профилирование, и меньше ломает event loop):

Пример (в реальном проде обычно запускают отдельно, через sidecar/агент):

code
py-spy top --pid <PID> --rate 100
py-spy record -o report.svg --pid <PID> --duration 30

Что это даёт:

  • hotspots по CPU: функции, на которых тратится вычислительное время;
  • косвенно — подтверждает, что проблема не только “ожидание”.

Если CPU низкий, а p99 высокий, CPU-профиль может показать мало интересного — тогда ставка на трассы и “ожидания”.

3.2. Явные таймеры по шагам (low friction, но очень полезно)

Добавьте ручные измерения для ключевых участков. Например, измерьте отдельно:

  • время валидации тела (можно частично через middleware),
  • время выполнения бизнес-логики,
  • время вызова БД,
  • время сериализации.

Пример с middleware (идея, адаптируйте под ваш стек логирования):

code
import time
from fastapi import Request, Response
from starlette.middleware.base import BaseHTTPMiddleware

class TimingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        t0 = time.perf_counter()
        response: Response = await call_next(request)
        t1 = time.perf_counter()
        # Здесь чаще фиксируют total latency; детальный разбор — ниже через трассы/метрики
        request_id = request.headers.get("x-request-id", "-")
        print(f"[{request_id}] {request.method} {request.url.path} total_ms={(t1-t0)*1000:.2f}")
        return response

Это не заменяет tracing, но помогает быстро увидеть, что «всё время ушло в total» и на каких маршрутах.

3.3. Инструментирование ожиданий: важный трюк

Для async-сервиса полезно разделять:

  • время активной обработки (CPU work);
  • время ожидания I/O.

В трассах это обычно видно как время span. Но если у вас tracing частично, можно делать измерение вокруг await:

code
import time
from contextlib import contextmanager

@contextmanager
def timer():
    start = time.perf_counter()
    yield lambda: (time.perf_counter() - start)

# Пример вокруг DB вызова
async def fetch_user(session, user_id: str):
    with timer() as end_timer:
        result = await session.execute(
            # ...
        )
        rows = result.fetchall()
    waited_s = end_timer()
    print(f"DB span ms={waited_s*1000:.2f}")
    return rows

Если такие “await-heavy” блоки соответствуют p99 — вы нашли ожидание, а не CPU.


4) Трассировки (tracing): как увидеть “где именно” теряется время

Профилирование отвечает на вопрос «где CPU». Tracing отвечает на вопрос «где запрос проводит время» — включая БД и внешние сервисы, если вы настроили инструментацию.

4.1. Почему tracing критичен именно для FastAPI

В одном запросе у вас может быть:

  • несколько обращений к БД,
  • несколько HTTP вызовов,
  • долгий сериализационный этап.

Профилировщик CPU может показать пустоту, потому что процесс “ждёт”, а tracing покажет, что ожидание именно в SELECT или в вызове внешнего API.

4.2. Базовая схема: spans и кореляция

Правильная трассировка обычно работает так:

  • Входящий запрос в FastAPI создаёт root span.
  • Каждый вызов внешних компонентов (БД, HTTP-клиент, кеш) создаёт child spans.
  • Корреляция идёт через trace_id и span_id.

Если у вас OpenTelemetry (OTel) — чаще всего берут готовые инструменты:

  • instrumentation для FastAPI/Starlette,
  • SQLAlchemy/asyncpg/DB driver,
  • httpx/aiohttp.

4.3. Как выглядит расследование по трассам

Типичный сценарий:

  1. Откройте trace для p99 запроса.
  2. Посмотрите дерево spans:
    • root span: общий latency.
    • child span “validate request” (иногда отдельный).
    • child “db.query user” и его длительность.
    • child “http call X”.
    • child “serialize response”.

Если вы видите:

  • root span 1500ms,
  • db span 1200ms,
  • остальное по 50ms, — значит, проблема в БД/транзакциях/индексах, а не в Pydantic.

Если наоборот:

  • db spans по 5–20ms,
  • но serialize span — 900ms, — виновата сериализация/валидация или объём ответа.

4.4. Неочевидные ловушки трассировок

  1. Неполная инструментировка: вы видите only HTTP spans, а внутренний async SQL — без spans. Тогда вы ошибочно считаете, что “всё быстро”, когда на самом деле ждёте неинструментированные участки.
  2. Смешивание воркеров и trace context: если у вас нестандартная схема отправки задач, trace context может потеряться.
  3. Слишком мелкая детализация: огромное число spans делает трассы дорогими, а расследование — медленным.

Практика: определите 10–20 критичных точек (маршруты, операции БД, внешние сервисы) и инструментируйте их. Остальное — по необходимости.


5) Отличить медленную функцию от медленного ожидания: практические правила

Одна из самых частых ошибок при анализе — смотреть только “самые большие значения” без интерпретации.

5.1. Правило №1: медленное ожидание почти всегда “await внутри трассы”

Если трасса показывает span “db.query” или “http call” на сотни миллисекунд/секунды — это почти наверняка ожидание I/O. Даже если рядом есть “функция обработчика”, она не обязательно делает CPU.

5.2. Правило №2: медленная функция тянет CPU и обычно видна в CPU profile

Когда часть времени тратится на:

  • сериализацию больших моделей,
  • сложные валидаторы,
  • преобразования данных, вы увидите:
  • рост CPU,
  • и в CPU профиле соответствующие места.

5.3. Правило №3: если async блокируется синхронным кодом, это выглядит как “ожидание”, но является CPU-bound

Например, внутри async endpoint вы сделали:

  • синхронный запрос requests.get(...),
  • тяжёлое вычисление без await,
  • файловые операции в основном event loop, и event loop начинает “подвисать”.

На трассах это часто проявляется как длинный gap без I/O spans, а на профиле — как CPU внутри конкретной функции или системные вызовы.

Пример анти-паттерна:

code
from fastapi import FastAPI
import requests

app = FastAPI()

@app.get("/bad")
async def bad():
    # request blocking: остановит event loop до завершения
    r = requests.get("https://example.com")
    return {"len": len(r.text)}

Правильнее:

  • либо async HTTP-клиент,
  • либо вынести в threadpool:
code
import asyncio
import requests
from fastapi import FastAPI

app = FastAPI()

@app.get("/good")
async def good():
    def blocking():
        return requests.get("https://example.com").text
    text = await asyncio.to_thread(blocking)
    return {"len": len(text)}

6) Поиск медленных мест по слоям: методика расследования

Разберём “сквозной” процесс. Представьте, что на проде p95 на /orders/{id} вырос с 200ms до 1200ms.

6.1. Шаг 1: определите, какие запросы деградируют и есть ли корреляции

  • Смотрите разрез по маршрутам: только один endpoint или весь сервис?
  • Смотрите по внешним факторам: всплеск нагрузки, изменения конфигураций, релизы, сетевые инциденты.
  • Корреляция с БД: рост latency БД, рост lock waits, увеличение времени отдельных запросов.

6.2. Шаг 2: сравните CPU и I/O

  • CPU растёт? Тогда сериализация/валидация/логика.
  • CPU стабильный, а latency растёт — ждём I/O: БД/HTTP/кеш.

6.3. Шаг 3: откройте трассу “типичного” p95 и “самого плохого” p99

Сравните две трассы:

  • количество db calls (сильно ли изменилось?),
  • какие конкретно запросы стали медленнее (индексы? объём?),
  • появился ли новый внешний вызов или изменился размер ответа.

6.4. Шаг 4: на уровне БД — что искать

На стороне SQL обычно есть три крупных виновника:

  1. Отсутствие индексов по условиям фильтрации/джойнов.
  2. Непредсказуемые планы: функции в WHERE, casting типов, строковые преобразования.
  3. N+1: циклы с отдельными запросами на каждую сущность.

Если используете ORM, N+1 может быть “незаметным” до тех пор, пока объём данных не вырастет.

Пример N+1 (типичный):

code
# Псевдо-код: сначала получаем список пользователей,
# потом в цикле получаем их заказы отдельными запросами
users = await session.execute(select(User).limit(100))
for user in users.scalars():
    orders = await session.execute(select(Order).where(Order.user_id == user.id))

Как чинят:

  • объединяют запросы (join),
  • используют eager loading (selectinload/joinedload),
  • батчат операции.

6.5. Шаг 5: на уровне сериализации/валидации — что искать

Проблемы сериализации часто всплывают так:

  • в p99 много “CPU внутри endpoint”;
  • tracing может показывать “serialize response” как крупный span (если инструментирован).

Типовые причины:

  • огромные response_model с вложенными моделями;
  • лишние поля/DTO, которые не нужны клиенту;
  • использование валидаторов, которые выполняют дорогие операции на каждом запросе;
  • преобразования дат/decimal без необходимости;
  • отправка больших списков без пагинации.

Практический подход:

  1. Сравните размер ответа (количество объектов/полей).
  2. Отключите временно response_model или уменьшите поля (в тестовой ветке) — чтобы проверить гипотезу.
  3. В CPU profile посмотрите, “что” занимает место: Pydantic internals, ваши преобразования, JSON кодер.

7) Метрики и дизайн эксперимента: чтобы не лечить симптом

Производительность — область, где легко “улучшить не то”. Поэтому важно соблюдать дисциплину эксперимента:

  • Меняйте одну переменную: например, только индексы или только пагинацию.
  • Делайте A/B или канареечные релизы, если это возможно.
  • Сравнивайте не средние значения, а percentiles: p95/p99 важнее.

Особенно осторожно с:

  • увеличением пула соединений к БД без проверки (может увеличить нагрузку и ухудшить lock contention),
  • уменьшением таймаутов (может “спасти” p99, но повысит ошибки и ретраи),
  • кешированием без лимитов (может улучшить латентность, но привести к утечкам памяти).

8) Чек-лист диагностики на боевых метриках

Ниже — практический чек-лист, который удобно применять, когда прод “проседает”. Он рассчитан на ситуацию, когда tracing и профилирование доступны, но решение всё равно нужно принимать быстро.

8.1. Сбор фактов

  • Как изменились p95/p99 по конкретным маршрутам?
  • Есть ли корреляция с релизами/конфигами?
  • Растёт ли CPU процесса/воркеров?
  • Растёт ли время I/O (БД/HTTP-клиенты)?
  • Растёт ли число in-flight запросов или очереди?

8.2. Разделение “CPU-bound vs I/O-bound”

  • Если CPU высокое: открывайте CPU profile, ищите сериализацию/валидацию/вычисления.
  • Если CPU низкое: открывайте трассы и смотрите самые длинные spans (обычно db или external http).

8.3. Трассы: поиск “длинной ветки”

  • В p99 trace найдите span, который даёт основной вклад.
  • Сравните p95 vs p99: поменялось ли количество вызовов (db calls count), поменялась ли длительность конкретного вызова.
  • Проверьте, не потерялась ли контекстность (trace_id отсутствует в child spans).
  • Убедитесь, что сериализация/response model инструментированы (если нет — добавьте временно метки).

8.4. БД

  • Есть ли признаки N+1 (много одинаковых запросов на один endpoint)?
  • Смотрится ли план запроса: используются ли индексы?
  • Вырос ли размер выборки/джойнов?
  • Нет ли lock waits / рост транзакционного времени?
  • Правильные ли типы в фильтрах (cast в WHERE может ломать индексы)?

8.5. Серилизация и Pydantic

  • Увеличился ли размер ответа (кол-во объектов/полей)?
  • Есть ли дорогие валидаторы / root validators?
  • Не отправляете ли больше данных, чем нужно (уменьшение response_model на тесте — быстрый тест-гипотеза)?
  • Нет ли повторной сериализации (например, формирование dict -> JSON руками -> снова в response)?

8.6. Асинхронная корректность

  • Проверить, нет ли синхронного I/O в async пути (requests, время на файловые операции, heavy CPU).
  • Если есть heavy CPU — выделить в executor/процесс либо оптимизировать алгоритм.
  • Проверить, есть ли таймауты и дедлайны на внешних запросах.

8.7. Итог: план устранения и верификация

  • Сформулировать гипотезу “источник” (CPU сериализация / конкретный SELECT / конкретный внешний API).
  • Провести точечное исправление (индекс, eager loading, уменьшение schema, замена синхронного вызова).
  • Проверить эффект на p95/p99 и на ошибках (timeouts, 5xx), а не только на среднем.

Вывод

Отладка производительности FastAPI — это не “пощёлкать профайлером и посмотреть верхние функции”. Это системный процесс, в котором вы сначала классифицируете задержку (CPU-bound или I/O-bound), затем по трассам находите “самую длинную ветку”, и уже там делаете точечный разбор: БД (индексы/N+1/планы), сериализация и Pydantic (объём ответа/валидаторы/response_model), корректность async (исключить блокировки event loop).

Если хотите закрепить практику на примерах и выстроить устойчивый workflow от метрик к трассам и обратно, полезно дополнительно пройти структурированную программу по теме наблюдаемости и диагностики (например, через курс по профилированию и трассировкам: [ /course/ ]). Но даже без обучения ключевой навык один: всегда отделяйте ожидание от вычислений и привязывайте вывод к конкретному span/запросу/участку CPU — тогда проблема будет решаться предсказуемо, а не “угадыванием”.

Если расскажете, какой у вас источник трасс (Prometheus+Grafana, ELK, Datadog, Jaeger, OpenTelemetry) и какой стек БД/ORM, можно предложить более конкретный план диагностики под вашу конфигурацию и примерную структуру spans для FastAPI.

Войдите, чтобы поставить лайк и оставить комментарий.

Автор

$ sudo teach IT

Продолжите обучение

Все курсы
Python – для начинающих!

Python – для начинающих!

С нуля до профессионального уровня. Подходит для всех. Учитесь каждый день и овладейте самым популярным языком программирования.

Перейти к курсу
Discord-боты на Python: с нуля до PRO

Discord-боты на Python: с нуля до PRO

Свой Discord-бот за первый вечер, а к концу курса — бот для сервера с тикетами, экономикой, модерацией, уровнями и ИИ. Python и discord.py 2.7, slash-команды, Components V2, запуск 24/7.

Перейти к курсу
Ботостроение Telegram

Ботостроение Telegram

Лёгкий, быстрый и доступный способ познакомиться с миром ботостроения в Telegram. Видео, конспекты, практика и помощь – всё у нас на курсе.

Перейти к курсу
Solidity DLC - поймай, если сможешь

Solidity DLC - поймай, если сможешь

Аудит смарт-контрактов на реальных взломах. The DAO, Parity, Wormhole, Ronin, Euler и ещё 7 разборов: что пошло не так, сколько потеряли и что бы спасло. Атаки и защиты на Foundry, план входа в профессию аудитора.

Перейти к курсу

Другие статьи

FastAPI и фоновые задачи: когда лучше использовать BackgroundTasks, а когда очередь
fastapi

FastAPI и фоновые задачи: когда лучше использовать BackgroundTasks, а когда очередь

Поймём различия между синхронной обработкой, BackgroundTasks и внешними очередями. Разберём idempotency, ретраи и мониторинг фоновых процессов.

22 июля 2026 г.
1190
FastAPI для новичков: как организовать структуру проекта, чтобы не писать всё в одном файле
fastapi

FastAPI для новичков: как организовать структуру проекта, чтобы не писать всё в одном файле

Разложим приложение на роуты, сервисы и слой моделей, настроим зависимости и обработчики ошибок. Цель — база для дальнейшего роста без будущего “рефакторинга на боль”.

26 июля 2026 г.
950
Как выучить программирование самому и бесплатно: с чего начать и в каком порядке
выучить

Как выучить программирование самому и бесплатно: с чего начать и в каком порядке

Разберём понятный маршрут для новичка: как выбрать первый язык, что учить в первую очередь, какие мини-проекты делать и как не забросить занятия. Дам бесплатные ориентиры и план, чтобы знания складывались в портфолио.

21 сентября 2026 г.
2420
python

Python 3.15: ленивые импорты, frozendict, sentinel, JIT и жизнь без GIL на реальном FastAPI

Один сервис на FastAPI, код для 3.12 и 3.15 и честные замеры: старт в 2,5 раза быстрее, 8 потоков без GIL в 4 раза быстрее — и где 3.15 медленнее.

6 ноября 2026 г.
110
Какой первый проект выбрать новичку, чтобы не бросить обучение
первый

Какой первый проект выбрать новичку, чтобы не бросить обучение

Подберём 5–7 идей под уровень “с нуля”, объясним, что делать по шагам и как довести проект до результата без перегруза. В конце — как оформить мини-портфолио и что показать, даже если проект маленький.

23 сентября 2026 г.
2280
С чего начать учить Python, если вы вообще не программист: план на 30 минут в день
учить

С чего начать учить Python, если вы вообще не программист: план на 30 минут в день

Разберём, что установить и как устроить обучение: первые переменные, вывод текста, простые условия и циклы — на понятных примерах без “магии”. В конце соберёте мини-скрипт, который что-то делает по вводу пользователя.

6 ноября 2026 г.
180

Комментарии

Станьте первым в обсуждении

Поделитесь мнением о статье или задайте вопрос автору

Содержание

1) С чего начать: определить тип проблемы (CPU vs ожидание)1.1. Быстрый диагностический разрез по метрикам2) «Сверху вниз»: карта слоёв времени запроса в FastAPI2.1. HTTP/ASGI слой и обработка запроса2.2. Бизнес-слой (CPU или логика)2.3. Слой I/O: БД и внешние сервисы2.4. Сериализация и формирование ответа2.5. Где обычно прячется «медленное»3) Профилирование: что делать, когда неясно, где тормозит3.1. CPU-профиль в async-сервисе: осторожно3.2. Явные таймеры по шагам (low friction, но очень полезно)3.3. Инструментирование ожиданий: важный трюк4) Трассировки (tracing): как увидеть “где именно” теряется время4.1. Почему tracing критичен именно для FastAPI4.2. Базовая схема: spans и кореляция4.3. Как выглядит расследование по трассам4.4. Неочевидные ловушки трассировок5) Отличить медленную функцию от медленного ожидания: практические правила5.1. Правило №1: медленное ожидание почти всегда “await внутри трассы”5.2. Правило №2: медленная функция тянет CPU и обычно видна в CPU profile5.3. Правило №3: если async блокируется синхронным кодом, это выглядит как “ожидание”, но является CPU-bound6) Поиск медленных мест по слоям: методика расследования6.1. Шаг 1: определите, какие запросы деградируют и есть ли корреляции6.2. Шаг 2: сравните CPU и I/O6.3. Шаг 3: откройте трассу “типичного” p95 и “самого плохого” p996.4. Шаг 4: на уровне БД — что искать6.5. Шаг 5: на уровне сериализации/валидации — что искать7) Метрики и дизайн эксперимента: чтобы не лечить симптом8) Чек-лист диагностики на боевых метриках8.1. Сбор фактов8.2. Разделение “CPU-bound vs I/O-bound”8.3. Трассы: поиск “длинной ветки”8.4. БД8.5. Серилизация и Pydantic8.6. Асинхронная корректность8.7. Итог: план устранения и верификацияВывод