diff --git a/backend/open_webui/routers/files.py b/backend/open_webui/routers/files.py index 811c223aaa..b349eeacd5 100644 --- a/backend/open_webui/routers/files.py +++ b/backend/open_webui/routers/files.py @@ -613,9 +613,12 @@ async def get_file_process_status( id: str, stream: bool = Query(False), user=Depends(get_verified_user), - db: AsyncSession = Depends(get_async_session), ): - file = await Files.get_file_by_id(id, db=db) + # NOTE: We intentionally do NOT use Depends(get_async_session) here. + # Database operations manage their own short-lived sessions internally. + # Holding a session here would keep a connection for the entire stream + # (up to two hours) and exhaust the connection pool under concurrent load. + file = await Files.get_file_by_id(id) if not file: raise HTTPException( @@ -623,16 +626,13 @@ async def get_file_process_status( detail=ERROR_MESSAGES.NOT_FOUND, ) - if file.user_id == user.id or user.role == 'admin' or await has_access_to_file(id, 'read', user, db=db): + if file.user_id == user.id or user.role == 'admin' or await has_access_to_file(id, 'read', user): if stream: MAX_FILE_PROCESSING_DURATION = 3600 * 2 async def event_stream(file_id): - # NOTE: We intentionally do NOT capture the request's db session here. - # Each poll creates its own short-lived session to avoid holding a - # connection for hours. A WebSocket push would be more efficient. for _ in range(MAX_FILE_PROCESSING_DURATION): - file_item = await Files.get_file_by_id(file_id) # Creates own session + file_item = await Files.get_file_by_id(file_id) if file_item: data = file_item.model_dump().get('data', {}) status = data.get('status') diff --git a/backend/open_webui/routers/knowledge.py b/backend/open_webui/routers/knowledge.py index 8ff3297681..d8f64d2181 100644 --- a/backend/open_webui/routers/knowledge.py +++ b/backend/open_webui/routers/knowledge.py @@ -1248,7 +1248,6 @@ async def get_pending_knowledge_files( id: str, stream: bool = Query(False), user=Depends(get_verified_user), - db: AsyncSession = Depends(get_async_session), ): """Return files that are being processed for this knowledge base but not yet linked. @@ -1261,7 +1260,11 @@ async def get_pending_knowledge_files( When ``stream=true``, returns an SSE stream that polls every 3 seconds and emits the current pending file list. Closes when no files remain. """ - knowledge = await Knowledges.get_knowledge_by_id(id=id, db=db) + # NOTE: We intentionally do NOT use Depends(get_async_session) here. + # Database operations manage their own short-lived sessions internally. + # Holding a session here would keep a connection for the entire stream + # (up to an hour) and exhaust the connection pool under concurrent load. + knowledge = await Knowledges.get_knowledge_by_id(id=id) if not knowledge: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, @@ -1276,7 +1279,6 @@ async def get_pending_knowledge_files( resource_type='knowledge', resource_id=knowledge.id, permission='read', - db=db, ) ): raise HTTPException( @@ -1285,7 +1287,7 @@ async def get_pending_knowledge_files( ) if not stream: - return await Files.get_pending_files_for_knowledge(id, db=db) + return await Files.get_pending_files_for_knowledge(id) async def event_stream(knowledge_id: str): MAX_POLL_DURATION = 3600 # 1 hour max