From 721b955c2e5d286db0c2bd7e417fbfbccd191135 Mon Sep 17 00:00:00 2001 From: AndreKanmegne Date: Mon, 15 Jun 2026 09:32:15 +0200 Subject: [PATCH] fix(rag): add sse keepalive heartbeat to prevent nginx 60s timeout (#434) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the embedding phase (multi-query expansion via cpu-only ollama) can take several minutes. nginx's proxy_read_timeout (60s default) was killing the idle sse connection before any event was flushed, leaving the ui stuck on "retrieving context…" forever. send a sse comment (: keepalive) every 20s to keep the connection alive through the entire pipeline duration. --- backend/controllers/ragController.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/backend/controllers/ragController.js b/backend/controllers/ragController.js index 81d3503..5983e70 100644 --- a/backend/controllers/ragController.js +++ b/backend/controllers/ragController.js @@ -84,6 +84,13 @@ export const askQuestionStream = catchAsync(async (req, res) => { closed = true; }); + // Keep the SSE connection alive through nginx's proxy_read_timeout (default 60s). + // The embedding phase can take minutes on CPU-only ollama — without this, nginx + // kills the idle connection before the first event is ever flushed. + const heartbeat = setInterval(() => { + if (!closed && !res.writableEnded) res.write(': keepalive\n\n'); + }, 20000); + try { await executeRAG({ question, @@ -104,6 +111,7 @@ export const askQuestionStream = catchAsync(async (req, res) => { send('error', { message: 'Stream failed', code: 'STREAM_ERROR' }); } } finally { + clearInterval(heartbeat); if (!res.writableEnded) res.end(); } });