fix: stream GET /chats/all to prevent OOM on large chat histories (#24461)

Convert the /chats/all endpoint from loading all user chats into memory
at once to a streaming NDJSON response that fetches chats in batches of
100. This prevents Out-of-Memory crashes for users with large chat
histories.

Backend: Added async generator that paginates through chats with
short-lived DB sessions per batch (critical for SQLite lock release).

Frontend: Updated getAllChats to consume the NDJSON stream via
ReadableStream reader, accumulating results for the export file.

Ref: open-webui#22206
This commit is contained in:
Classic298
2026-05-09 04:11:52 +09:00
committed by GitHub
parent 6082e1adae
commit 55d1db1f38
2 changed files with 78 additions and 24 deletions
+39 -4
View File
@@ -676,11 +676,46 @@ async def get_user_pinned_chats(user=Depends(get_verified_user), db: AsyncSessio
# GetChats
############################
CHAT_EXPORT_BATCH_SIZE = 100
@router.get('/all', response_model=list[ChatResponse])
async def get_user_chats(user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
result = await Chats.get_chats_by_user_id(user.id, db=db)
return [ChatResponse(**chat.model_dump()) for chat in result.items]
async def generate_chat_export_ndjson(user_id: str):
"""
Async generator that streams all user chats as NDJSON (one JSON object per line).
Uses short-lived DB sessions per batch to avoid holding locks for the
entire duration, which is critical for SQLite environments.
"""
skip = 0
while True:
result = await Chats.get_chats_by_user_id(
user_id,
skip=skip,
limit=CHAT_EXPORT_BATCH_SIZE,
db=None,
)
if not result.items:
break
for chat in result.items:
try:
yield ChatResponse(**chat.model_dump()).model_dump_json() + '\n'
except Exception as e:
log.exception(f'Error serializing chat {chat.id}: {e}')
if len(result.items) < CHAT_EXPORT_BATCH_SIZE:
break
skip += CHAT_EXPORT_BATCH_SIZE
@router.get('/all')
async def get_user_chats(user=Depends(get_verified_user)):
return StreamingResponse(
generate_chat_export_ndjson(user.id),
media_type='application/x-ndjson',
)
############################