Enhance session handling in SessionRoutes

- Improved logging for session aborts and unexpected exits.
- Introduced a variable to track if the session was aborted for clarity.
- Added logic to create a new AbortController when restarting the generator after a crash.
- Implemented a mechanism to abort the session if there are no pending tasks after a natural completion.
- Ensured that errors during recovery checks lead to session abortion to prevent resource leaks.
This commit is contained in:
Alex Newman
2025-12-31 16:49:17 -05:00
parent fef332d213
commit de20eb65b5
2 changed files with 78 additions and 66 deletions
@@ -168,8 +168,9 @@ export class SessionRoutes extends BaseRouteHandler {
})
.finally(() => {
const sessionDbId = session.sessionDbId;
if (session.abortController.signal.aborted) {
const wasAborted = session.abortController.signal.aborted;
if (wasAborted) {
logger.info('SESSION', `Generator aborted`, { sessionId: sessionDbId });
} else {
logger.warn('SESSION', `Generator exited unexpectedly`, { sessionId: sessionDbId });
@@ -180,16 +181,20 @@ export class SessionRoutes extends BaseRouteHandler {
this.workerService.broadcastProcessingStatus();
// Crash recovery: If not aborted and still has work, restart
if (!session.abortController.signal.aborted) {
if (!wasAborted) {
try {
const pendingStore = this.sessionManager.getPendingMessageStore();
const pendingCount = pendingStore.getPendingCount(sessionDbId);
if (pendingCount > 0) {
logger.info('SESSION', `Restarting generator after crash/exit with pending work`, {
sessionId: sessionDbId,
pendingCount
});
// Create new AbortController for the restarted generator
session.abortController = new AbortController();
// Small delay before restart
setTimeout(() => {
const stillExists = this.sessionManager.getSession(sessionDbId);
@@ -197,12 +202,19 @@ export class SessionRoutes extends BaseRouteHandler {
this.startGeneratorWithProvider(stillExists, this.getSelectedProvider(), 'crash-recovery');
}
}, 1000);
} else {
// No pending work - abort to kill the child process
session.abortController.abort();
logger.debug('SESSION', 'Aborted controller after natural completion', {
sessionId: sessionDbId
});
}
} catch (e) {
// Ignore errors during recovery check
// Ignore errors during recovery check, but still abort to prevent leaks
session.abortController.abort();
}
}
// NOTE: We do NOT delete the session here anymore.
// NOTE: We do NOT delete the session here anymore.
// The generator waits for events, so if it exited, it's either aborted or crashed.
// Idle sessions stay in memory (ActiveSession is small) to listen for future events.
});