Какие способы получения доступа к долгоживущим объектам используются в FastAPI?
Что проверяют
Понимаете ли вы разницу между объектом на один запрос и объектом, который живёт столько же, сколько приложение: пул соединений с базой, HTTP-клиент, загруженная ML-модель. Следом обычно спрашивают, как этот объект подменить в тестах и что с ним происходит при нескольких воркерах.
Как отвечать
Основной путь: создать объект в lifespan и достать его через зависимость.
- lifespan: асинхронный контекстный менеджер, передаётся в
FastAPI(lifespan=...). Код доyieldвыполняется при старте, после него при остановке. Декоратор@app.on_event("startup")устарел; app.state: lifespan кладёт туда созданный объект, обработчик берёт его какrequest.app.state.pool. Ещё lifespan может сделатьyield {"pool": pool}, и значения станут доступны вrequest.state;- зависимость поверх state:
def get_pool(request: Request)возвращает пул, обработчик получает его черезAnnotated[Pool, Depends(get_pool)]. Отсюда точка подмены в тестах:app.dependency_overrides[get_pool] = fake_pool; - зависимость с
yield: для короткоживущего ресурса поверх долгоживущего, например сессия из пула, которая закрывается вfinallyпосле ответа; lru_cacheна фабрике: так документация FastAPI советует отдаватьSettingsиз pydantic-settings, объект создаётся один раз. Внутри одного запроса результат зависимости и так кэшируется (use_cache=True).
Отдельно проговорите процессы: uvicorn --workers 4 или gunicorn запускают отдельные процессы, и у каждого своя копия объекта. Общее состояние между ними живёт только снаружи, в Redis или базе.
Где спотыкаются
Создают клиента на каждый запрос: httpx.AsyncClient() в обработчике или create_engine в зависимости. Пул соединений теряется, растут задержки, под нагрузкой кончаются сокеты.
Предлагают Singleton через __new__ или глобальную переменную модуля. Такой объект никто не закроет при остановке, а асинхронный пул вроде asyncpg.create_pool при импорте вообще не создать: ему нужен запущенный event loop.
Считают app.state общим хранилищем и держат в нём счётчики или кэш пользовательских данных. С несколькими воркерами каждый процесс видит своё, и данные расходятся.