57a60c1309
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
4.2 KiB
4.2 KiB
Plan: Address PR #856 Review Feedback
Summary of Review Feedback
Multiple reviewers identified the same core issues:
-
Race Condition in Idle Detection (Medium-High Priority)
- When timeout fires at 3:00 but last message was at 2:59,
idleDurationis 1 second, check fails - Need to either remove redundant check or reset
lastActivityTimeon timeout
- When timeout fires at 3:00 but last message was at 2:59,
-
Missing Test Coverage (High Priority)
- No tests for SessionQueueProcessor timeout logic
- Critical fix for high-impact bug (79 processes, 13.4GB swap)
-
Minor: Optional Chaining (Low Priority)
- Use
onIdleTimeout?.()instead ofif (onIdleTimeout) { onIdleTimeout() }
- Use
-
Minor: Logging Enhancement (Low Priority)
- Add timeout threshold to log message for debugging
Phase 0: Documentation Discovery (COMPLETE)
Sources Consulted
- PR #856 comments from claude, greptile-apps reviewers
src/services/queue/SessionQueueProcessor.ts(current implementation)
Allowed APIs
waitForMessage(signal, timeoutMs)→ Promiselogger.info('SESSION', ...)for logging
The Fix Strategy
The reviewers suggest two options:
Option A: Remove redundant check since waitForMessage enforces timeout
if (!receivedMessage && !signal.aborted) {
// Timeout occurred - exit gracefully
const idleDuration = Date.now() - lastActivityTime;
logger.info('SESSION', 'Exiting queue iterator due to idle timeout', { ... });
onIdleTimeout?.();
return;
}
Option B: Reset lastActivityTime on timeout to handle edge cases
if (!receivedMessage && !signal.aborted) {
const idleDuration = Date.now() - lastActivityTime;
if (idleDuration >= IDLE_TIMEOUT_MS) {
logger.info('SESSION', 'Exiting...', { ... });
onIdleTimeout?.();
return;
}
// CRITICAL: Reset timer since we know queue is empty now
lastActivityTime = Date.now();
}
Decision: Use Option B - it's defensive and handles spurious wakeups correctly.
Phase 1: Fix Race Condition in SessionQueueProcessor
What to Implement
Fix the idle timeout logic to reset lastActivityTime when timeout occurs but duration check fails.
Tasks
- In
createIterator()at lines 50-62, addlastActivityTime = Date.now()after the duration check fails - Use optional chaining for
onIdleTimeout?.() - Add timeout threshold to log message
Pattern to Follow
if (!receivedMessage && !signal.aborted) {
const idleDuration = Date.now() - lastActivityTime;
if (idleDuration >= IDLE_TIMEOUT_MS) {
logger.info('SESSION', 'Idle timeout reached, triggering abort to kill subprocess', {
sessionDbId,
idleDurationMs: idleDuration,
thresholdMs: IDLE_TIMEOUT_MS
});
onIdleTimeout?.();
return;
}
// Reset timer on spurious wakeup - queue is empty but duration check failed
lastActivityTime = Date.now();
}
Verification
npm run build
grep -A10 "idleDuration >= IDLE_TIMEOUT_MS" src/services/queue/SessionQueueProcessor.ts
Phase 2: Add Unit Tests for SessionQueueProcessor
What to Implement
Create test file covering the idle timeout behavior.
Test Cases Required
- Iterator exits after idle timeout when no messages arrive
onIdleTimeoutcallback is invoked on timeout- Message arrival resets the idle timer
- Abort signal takes precedence over timeout
- Event listener cleanup happens correctly
Location
tests/services/queue/SessionQueueProcessor.test.ts
Verification
npm run test -- SessionQueueProcessor
Phase 3: Build and Verify
Tasks
- Run
npm run build- verify no TypeScript errors - Run tests to ensure timeout behavior works
- Commit changes to fix/observer-idle-timeout branch
- Push to update PR #856
Verification
npm run build
npm run test
git diff --stat
Phase 4: Update PR Description
Tasks
- Update test plan checkboxes in PR description
- Add note about race condition fix
Summary of Changes
| File | Change |
|---|---|
src/services/queue/SessionQueueProcessor.ts |
Fix race condition, optional chaining, enhanced logging |
tests/services/queue/SessionQueueProcessor.test.ts |
New test file for timeout behavior |