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

启用SQLAdmin AuthenticationBackend后垃圾回收器警告问题求助

问题:SQLAlchemy连接未正确回收引发垃圾回收警告

警告信息

The garbage collector is trying to clean up non-checked-in connection <AdaptedConnection <Connection(Thread-3, started daemon 15844)>>, which will be dropped, as it cannot be safely terminated.  Please ensure that SQLAlchemy pooled connections are returned to the pool explicitly, either by calling ``close()`` or by using appropriate context managers to manage their lifecycle.
C:\mSpaceLock\mSpaceLock-www\env\Lib\site-packages\jinja2\nodes.py:176: SAWarning: The garbage collector is trying to clean up non-checked-in connection <AdaptedConnection <Connection(Thread-3, started daemon 15844)>>, which will be dropped, as it cannot be safely terminated.  Please ensure that SQLAlchemy pooled connections are returned to the pool explicitly, either by calling ``close()`` or by using appropriate context managers to manage their lifecycle.

场景说明

基于FastAPI + SQLAdmin的应用,authenticate_user函数需要接收AsyncSession参数:

async def authenticate_user(
    username: str,
    password: str,
    session: AsyncSession = Depends(get_async_session),
):
    user = await get_user(username=username, session=session)
    if not user:
        return False
    if not Hasher.verify_password(password, user.hashed_password):
        return False
    return user

在常规FastAPI端点中,通过Depends(get_async_session)获取session不会触发警告:

@router.post("/token", response_model=Token)
async def login_for_access_token(
    response: Response,
    form_data: OAuth2PasswordRequestForm = Depends(),
    session: AsyncSession = Depends(get_async_session),
):
    user = await authenticate_user(form_data.username, form_data.password, session)

但在SQLAdmin的AuthenticationBackend.login方法中,直接调用async_session_maker()创建session并传入authenticate_user后,触发了上述警告:

class AdminAuth(AuthenticationBackend):
    async def login(
        self,
        request: Request,
    ) -> bool:
        form = await request.form()
        username, password = form["username"], form["password"]
        user = await authenticate_user(
            username, password, session=async_session_maker()
        )
        if not user:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Incorrect username or password",
            )
        if not user.superuser:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="You are not permitted!!!",
            )
        admin_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
        admin_token = create_access_token(
            data={"sub": user.email}, expires_delta=admin_token_expires
        )
        request.session.update({"admin_token": admin_token})
        return True

Session配置如下:

SQLALCHEMY_DATABASE_URL = "sqlite+aiosqlite:///./sql_app.sqlite"
engine = create_async_engine(
    SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)

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:
        try:
            yield session
        finally:
            await session.close()

已尝试在get_async_session中添加finally: await session.close(),但警告仍存在。


解决建议

核心问题

直接调用async_session_maker()创建的session没有被正确关闭——既没有用上下文管理器(async with)包裹,也没有手动调用close(),导致连接无法回到连接池,最终被垃圾回收器处理时触发警告。

修复方法

在login方法中,用async with上下文管理器创建并管理session的生命周期,确保连接自动回收:

class AdminAuth(AuthenticationBackend):
    async def login(
        self,
        request: Request,
    ) -> bool:
        form = await request.form()
        username, password = form["username"], form["password"]
        
        # 使用async with管理session生命周期
        async with async_session_maker() as session:
            user = await authenticate_user(username, password, session=session)
            if not user:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Incorrect username or password",
                )
            if not user.superuser:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="You are not permitted!!!",
                )
        
        # Token生成逻辑不受session影响,可放在上下文外
        admin_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
        admin_token = create_access_token(
            data={"sub": user.email}, expires_delta=admin_token_expires
        )
        request.session.update({"admin_token": admin_token})
        return True

额外说明

  • async_session_maker()创建的session必须通过async with或者手动await session.close()来确保连接被回收;
  • 常规FastAPI端点中用Depends(get_async_session)没问题,因为get_async_session通过async with和finally保证了session会被关闭;
  • 若需手动管理session,也可以在使用完session后调用await session.close(),但上下文管理器是更安全、简洁的方式。

内容的提问来源于stack exchange,提问作者Szymon Klause

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 10:43:21