From ecb2aeacf9569bb2ea4d678a97c527234aefd159 Mon Sep 17 00:00:00 2001 From: Luca <102960244+Luca-Timo@users.noreply.github.com> Date: Fri, 29 May 2026 15:41:58 +0200 Subject: [PATCH] fix(test-infra): unref sessionTimeout cleanup interval so workers exit gracefully MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 5-minute session-sweep interval at sessionTimeout.js:17 fired at module-load time without .unref(), so every jest worker that transitively required this module (server.js → middleware → most of the route layer) kept the event loop alive forever. The worker then got force-killed on shutdown, surfacing as the longstanding "worker failed to exit gracefully" warning at the end of every CI run on upstream/beta. Under enough I/O / memory pressure on a CI runner, the force-kill could land MID-test rather than after the suite finished, taking out whatever else was running on that worker — most visibly integration/storageBackend.test.js on PR #555's runs. .unref() makes the timer not keep the loop alive on its own. Production behaviour is unchanged: the timer still fires every 5 min as long as anything else is holding the loop open (the HTTP server, always). --- backend/src/middleware/sessionTimeout.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/backend/src/middleware/sessionTimeout.js b/backend/src/middleware/sessionTimeout.js index 462cd490..3c7968bb 100644 --- a/backend/src/middleware/sessionTimeout.js +++ b/backend/src/middleware/sessionTimeout.js @@ -13,7 +13,18 @@ let cachedTimeout = null; let cacheExpiry = 0; const CACHE_DURATION = 30 * 60 * 1000; // 30 minutes - reduced DB queries -// Clean up expired sessions every 5 minutes +// Clean up expired sessions every 5 minutes. +// +// `.unref()` so this timer doesn't keep the event loop alive on its +// own — without it, every jest worker that requires this module +// (directly or transitively via server.js / a middleware-importing +// route file) gets stuck and either prints the "worker failed to +// exit gracefully" warning or, under high CI load, force-kills mid- +// test and takes an unrelated suite down with it (we hit this with +// integration/storageBackend.test.js on PR #555). Production +// behaviour is unchanged: the timer fires every 5 min as long as +// the server has anything else keeping the loop alive (HTTP server, +// other intervals), which is always. setInterval(() => { const now = Date.now(); for (const [token, lastActivity] of sessions.entries()) { @@ -21,7 +32,7 @@ setInterval(() => { sessions.delete(token); } } -}, 5 * 60 * 1000); +}, 5 * 60 * 1000).unref(); async function getSessionTimeout() { const now = Date.now();