You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FastAPI Lifespan关闭会话触发AttributeError问题排查与解决

问题:FastAPI服务器关闭时优雅释放资源失败的类型错误分析与解决

依赖版本

  • fastapi(0.95.0)
  • fastapi-users(10.4.2)
  • fastapi-users-db-sqlalchemy(5.0.0)
  • SQLAlchemy(2.0.10)

简化代码片段

engine = create_async_engine(SQLALCHEMY_DATABASE_URL)
async_session_maker = async_sessionmaker(engine, expire_on_commit=False)


async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
    async with async_session_maker() as session:
        yield session


async def get_user_db(session: AsyncSession = Depends(get_async_session)):
    yield SQLAlchemyUserDatabase(session, UserModel, OAuthAccount)


@asynccontextmanager
async def lifespan(fapp: FastAPI):
    # establish a connection to the database    
    fapp.state.async_session = await get_user_db().__anext__()
    yield
    # close the connection to the database
    await fapp.state.async_session.close()
    await fapp.state.async_session.engine.dispose()


app = FastAPI(lifespan=lifespan)

# Add Routes
# ...

if __name__ == '__main__':
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

错误现象

第一次错误(直接调用close())

按下Ctrl-C停止服务器时出现以下错误:

INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
^CINFO:     Shutting down
INFO:     Waiting for application shutdown.
<class 'fastapi_users_db_sqlalchemy.SQLAlchemyUserDatabase'>
ERROR:    Traceback (most recent call last):
  File "/path/to/proj/env/lib/python3.10/site-packages/starlette/routing.py", line 677, in lifespan
    async with self.lifespan_context(app) as maybe_state:
  File "/usr/lib/python3.10/contextlib.py", line 206, in __aexit__
    await anext(self.gen)
  File "/path/to/proj/src/main.py", line 40, in lifespan
    await fastapi_app.state.async_session.close()
AttributeError: 'SQLAlchemyUserDatabase' object has no attribute 'close'

ERROR:    Application shutdown failed. Exiting.
INFO:     Finished server process [37752]

第二次错误(调用session.close())

修改为await fapp.state.async_session.session.close()后,再次停止服务器出现:

INFO:     Started server process [33125]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
^CINFO:     Shutting down
INFO:     Waiting for application shutdown.
ERROR:    Traceback (most recent call last):
  File "/path/to/proj/env/lib/python3.10/site-packages/starlette/routing.py", line 677, in lifespan
    async with self.lifespan_context(app) as maybe_state:
  File "/path/to/proj/env/lib/python3.10/site-packages/starlette/routing.py", line 569, in __aexit__
    await self._router.shutdown()
  File "/path/to/proj/env/lib/python3.10/site-packages/starlette/routing.py", line 664, in shutdown
    await handler()
  File "/path/to/proj/src/main.py", line 88, in shutdown
    await app.state.async_session.session.close()
AttributeError: 'Depends' object has no attribute 'close'

ERROR:    Application shutdown failed. Exiting.

错误原因分析

  1. 第一个错误根源:get_user_db()是生成器函数,调用__anext__()获取的是SQLAlchemyUserDatabase实例,而非AsyncSession。这个类仅封装数据库会话,本身没有close()方法,直接调用会报错。
  2. 第二个错误根源:手动调用get_user_db()时未传入实际的AsyncSession参数,导致SQLAlchemyUserDatabase内部的session属性被赋值为Depends(get_async_session)对象(而非真实会话实例),因此访问session.close()会触发Depends对象无该属性的错误。get_user_db设计用于FastAPI的依赖注入系统,手动调用无法自动解析Depends参数。

解决方案

修正lifespan函数,直接管理AsyncSession和engine的生命周期,避免通过依赖注入函数手动获取数据库会话:

engine = create_async_engine(SQLALCHEMY_DATABASE_URL)
async_session_maker = async_sessionmaker(engine, expire_on_commit=False)


async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
    async with async_session_maker() as session:
        yield session


async def get_user_db(session: AsyncSession = Depends(get_async_session)):
    yield SQLAlchemyUserDatabase(session, UserModel, OAuthAccount)


@asynccontextmanager
async def lifespan(fapp: FastAPI):
    # 直接创建AsyncSession实例
    async with async_session_maker() as session:
        fapp.state.async_session = session
        # 若需全局可用的user_db实例,可在此创建并存入state
        fapp.state.user_db = SQLAlchemyUserDatabase(session, UserModel, OAuthAccount)
        yield
        # 关闭会话
        await session.close()
    # 释放数据库引擎资源
    await engine.dispose()


app = FastAPI(lifespan=lifespan)

# Add Routes
# ...

if __name__ == '__main__':
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

关键修正点

  • 直接通过async_session_maker()创建AsyncSession,绕开依赖注入函数的手动调用问题,确保拿到真实的会话实例。
  • 关闭资源顺序:先关闭会话,再调用engine.dispose()释放所有连接池资源,保证优雅 shutdown。
  • 原有的get_user_db函数仍可用于路由的依赖注入,不影响业务逻辑。

内容的提问来源于stack exchange,提问作者Homunculus Reticulli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 11:27:46