feat(FN-1607): add process lifecycle diagnostics and fix resource leaks

- Add process lifecycle diagnostics for dashboard and serve commands
- Add SQLite database health check to diagnostics endpoint
- Add store listener count diagnostics for debugging subscription leaks
- Audit and fix SSE connection management to prevent connection leaks
- Audit and fix timer/interval cleanup in engine and CLI shutdown handlers
- Fix res.on() call guard for test mocks compatibility
- Fix variable declaration ordering in serve.ts
- Update memory with diagnostic findings for future debugging
This commit is contained in:
gsxdsm
2026-04-12 15:27:50 -07:00
parent 1b9421561a
commit 9b20a99bae
5 changed files with 472 additions and 2 deletions

View File

@@ -3,12 +3,18 @@ import type { TaskStore, MissionStore, PluginStore, PluginInstallation, PluginSt
import type { AiSessionStore } from "./ai-session-store.js";
let activeConnections = 0;
let highWaterMark = 0;
/** Returns the current number of active SSE connections. */
export function getActiveSSEConnections(): number {
return activeConnections;
}
/** Returns the high water mark of SSE connections. */
export function getSSEHighWaterMark(): number {
return highWaterMark;
}
/**
* Safely write to an SSE response stream.
* Returns `true` if the write succeeded, `false` if the connection is dead.
@@ -177,6 +183,11 @@ export function createSSE(
res.flushHeaders();
activeConnections++;
// Track high water mark and log when new highs are reached
if (activeConnections > highWaterMark) {
highWaterMark = activeConnections;
console.log(`[sse] active connections: ${activeConnections} (high water mark: ${highWaterMark})`);
}
// Send initial heartbeat
res.write(": connected\n\n");
@@ -415,6 +426,15 @@ export function createSSE(
send("event: heartbeat\ndata: \n\n");
}, 30_000);
// Register cleanup on request close (primary path for HTTP/1.1)
_req.on("close", cleanup);
// Also register on response close as a safety net for edge cases
// (e.g., proxy timeouts, HTTP/2 stream resets). This ensures cleanup
// fires even if the request object doesn't emit "close".
// Guard with typeof check for test mocks that may not have on method.
if (typeof res.on === "function") {
res.on("close", cleanup);
}
};
}