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:
Alex Newman
2025-12-28 16:28:58 -05:00
parent 06739cfdfa
commit b8ce27bd31
7 changed files with 312 additions and 244 deletions
+34 -10
View File
@@ -82,17 +82,41 @@ export class PendingMessageStore {
}
/**
* Peek at oldest pending message for session (does NOT change status)
* @returns The oldest pending message or null if none
* Atomically claim the next pending message for processing
* Finds oldest pending -> marks processing -> returns it
* Uses a transaction to prevent race conditions
*/
peekPending(sessionDbId: number): PersistentPendingMessage | null {
const stmt = this.db.prepare(`
SELECT * FROM pending_messages
WHERE session_db_id = ? AND status = 'pending'
ORDER BY id ASC
LIMIT 1
`);
return stmt.get(sessionDbId) as PersistentPendingMessage | null;
claimNextMessage(sessionDbId: number): PersistentPendingMessage | null {
const now = Date.now();
const claimTx = this.db.transaction((sessionId: number, timestamp: number) => {
const peekStmt = this.db.prepare(`
SELECT * FROM pending_messages
WHERE session_db_id = ? AND status = 'pending'
ORDER BY id ASC
LIMIT 1
`);
const msg = peekStmt.get(sessionId) as PersistentPendingMessage | null;
if (msg) {
const updateStmt = this.db.prepare(`
UPDATE pending_messages
SET status = 'processing', started_processing_at_epoch = ?
WHERE id = ?
`);
updateStmt.run(timestamp, msg.id);
// Return updated object
return {
...msg,
status: 'processing',
started_processing_at_epoch: timestamp
} as PersistentPendingMessage;
}
return null;
});
return claimTx(sessionDbId, now) as PersistentPendingMessage | null;
}
/**