feat(queue): Simplify queue processing and enhance reliability
- Implemented atomic message claiming in PendingMessageStore with claimNextMessage. - Removed obsolete peekPending method to streamline message retrieval. - Introduced SessionQueueProcessor for robust async message iteration, replacing complex polling logic. - Refactored SessionManager to eliminate in-memory queue state, relying on PendingMessageStore for message tracking. - Cleaned up session handling logic, removing recursive restarts and session deletion on empty queues. - Enhanced error handling and logging for generator failures and session processing. - Updated SessionRoutes to handle crash recovery more effectively without deleting sessions.
This commit is contained in:
@@ -690,24 +690,25 @@ export class WorkerService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a session with auto-restart and cleanup logic
|
||||
* Recursively restarts the generator if there's pending work
|
||||
* Start a session processor
|
||||
* It will run continuously until the session is deleted/aborted
|
||||
*/
|
||||
private startSessionWithAutoRestart(
|
||||
private startSessionProcessor(
|
||||
session: ReturnType<typeof this.sessionManager.getSession>,
|
||||
getPendingCount: (sid: number) => number,
|
||||
source: string
|
||||
): void {
|
||||
if (!session) return;
|
||||
|
||||
const sid = session.sessionDbId;
|
||||
logger.info('SYSTEM', `Starting generator (${source})`, {
|
||||
sessionId: sid,
|
||||
pendingCount: getPendingCount(sid)
|
||||
sessionId: sid
|
||||
});
|
||||
|
||||
session.generatorPromise = this.sdkAgent.startSession(session, this)
|
||||
.catch(error => {
|
||||
// Only log if not aborted
|
||||
if (session.abortController.signal.aborted) return;
|
||||
|
||||
logger.error('SYSTEM', `Generator failed (${source})`, {
|
||||
sessionId: sid,
|
||||
error: error.message
|
||||
@@ -716,24 +717,13 @@ export class WorkerService {
|
||||
.finally(() => {
|
||||
session.generatorPromise = null;
|
||||
this.broadcastProcessingStatus();
|
||||
|
||||
// Check if there's more work pending
|
||||
const stillPending = getPendingCount(sid);
|
||||
if (stillPending > 0) {
|
||||
logger.info('SYSTEM', `Auto-restarting generator for pending work`, {
|
||||
sessionId: sid,
|
||||
pendingCount: stillPending
|
||||
});
|
||||
setTimeout(() => {
|
||||
const stillExists = this.sessionManager.getSession(sid);
|
||||
if (stillExists && !stillExists.generatorPromise) {
|
||||
// Recursive call for continuous processing
|
||||
this.startSessionWithAutoRestart(stillExists, getPendingCount, 'auto-restart');
|
||||
}
|
||||
}, 0);
|
||||
} else {
|
||||
// No more work - clean up session
|
||||
this.sessionManager.deleteSession(sid).catch(() => {});
|
||||
|
||||
// Crash recovery: if not aborted, check if we should restart
|
||||
if (!session.abortController.signal.aborted) {
|
||||
// We can check if there are pending messages to decide if restart is urgent
|
||||
// But generally, if it crashed, we might want to restart?
|
||||
// For now, let's just log. The user/system can trigger restart if needed.
|
||||
logger.warn('SYSTEM', `Session processor exited unexpectedly`, { sessionId: sid });
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -789,12 +779,8 @@ export class WorkerService {
|
||||
pendingCount: pendingStore.getPendingCount(sessionDbId)
|
||||
});
|
||||
|
||||
// Start SDK agent (non-blocking) using shared helper
|
||||
this.startSessionWithAutoRestart(
|
||||
session,
|
||||
(sid) => pendingStore.getPendingCount(sid),
|
||||
'startup-recovery'
|
||||
);
|
||||
// Start SDK agent (non-blocking)
|
||||
this.startSessionProcessor(session, 'startup-recovery');
|
||||
|
||||
result.sessionsStarted++;
|
||||
result.startedSessionIds.push(sessionDbId);
|
||||
|
||||
Reference in New Issue
Block a user