启用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
相关产品推荐
相关产品推荐

