🪂 feat: Graceful HTTP shutdown on SIGTERM/SIGINT (#13211)

* 🪂 feat: Graceful HTTP shutdown on SIGTERM/SIGINT

* Address feedback

* don't treat ERR_SERVER_NOT_RUNNING as fatal; route telemetry shutdown through coordinator
This commit is contained in:
Pete Hampton 2026-05-20 18:33:53 +01:00 committed by GitHub
parent 9dd062e42e
commit 679672ad15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 428 additions and 108 deletions

View file

@ -7,6 +7,7 @@ const {
sessionCache,
standardCache,
violationCache,
registerShutdownTask,
} = require('@librechat/api');
const namespaces = {
@ -195,23 +196,16 @@ if (!cacheConfig.USE_REDIS && !cacheConfig.CI) {
cleanupIntervals.add(monitor);
}
const dispose = () => {
// Register cleanup with the centralized graceful-shutdown coordinator
// (see packages/api/src/app/shutdown.ts) rather than attaching a direct
// signal handler — multiple competing handlers race the HTTP drain.
registerShutdownTask('cache cleanup', async () => {
cacheConfig.DEBUG_MEMORY_CACHE && console.log('[Cache] Cleaning up and shutting down...');
cleanupIntervals.forEach((interval) => clearInterval(interval));
cleanupIntervals.clear();
// One final cleanup before exit
clearAllExpiredFromCache().then(() => {
cacheConfig.DEBUG_MEMORY_CACHE && console.log('[Cache] Final cleanup completed');
process.exit(0);
});
};
// Handle various termination signals
process.on('SIGTERM', dispose);
process.on('SIGINT', dispose);
process.on('SIGQUIT', dispose);
process.on('SIGHUP', dispose);
await clearAllExpiredFromCache();
cacheConfig.DEBUG_MEMORY_CACHE && console.log('[Cache] Final cleanup completed');
});
}
/**

View file

@ -22,6 +22,7 @@ const {
createStreamServices,
initializeFileStorage,
preAuthTenantMiddleware,
setupGracefulShutdown,
updateInterfacePermissions,
} = require('@librechat/api');
const { connectDb, indexSync } = require('~/db');
@ -240,7 +241,7 @@ const startServer = async () => {
/** Error handler (must be last - Express identifies error middleware by its 4-arg signature) */
app.use(ErrorController);
app.listen(port, host, async (err) => {
const server = app.listen(port, host, async (err) => {
if (err) {
logger.error('Failed to start server:', err);
process.exit(1);
@ -283,6 +284,8 @@ const startServer = async () => {
process.exit(1);
}
});
setupGracefulShutdown(server);
};
/**