Skip to content

Fix slow shutdown on SIGINT/SIGTERM#325

Merged
brendanlong merged 3 commits intomainfrom
claude/fbc11e47-5de2-480b-afe4-e98a54bcb3a6
Apr 7, 2026
Merged

Fix slow shutdown on SIGINT/SIGTERM#325
brendanlong merged 3 commits intomainfrom
claude/fbc11e47-5de2-480b-afe4-e98a54bcb3a6

Conversation

@brendanlong
Copy link
Copy Markdown
Owner

Summary

  • Add SIGINT/SIGTERM signal handlers for graceful shutdown that stop active Claude queries and disconnect Prisma
  • Fix setInterval in rate-limiter keeping the Node.js event loop alive by adding .unref()
  • Add stopAllSessions() to claude-runner for bulk session cleanup during shutdown
  • A second Ctrl+C force-exits immediately

Root Causes

The process was hanging on Ctrl+C because:

  1. No signal handlers — there were no SIGINT/SIGTERM handlers, so Node.js relied on the event loop draining naturally
  2. Rate-limiter setInterval — the hourly cleanup timer (line 216 of rate-limiter.ts) was never .unref()'d, so it kept the event loop alive indefinitely
  3. Active Claude queries — running SDK queries weren't interrupted during shutdown
  4. Prisma connections — the database connection pool wasn't closed

Test plan

  • Unit tests pass (pnpm test:run — 361 tests)
  • Integration tests pass (pnpm test:integration — 143 tests)
  • Manual test: pnpm start, then Ctrl+C — should exit promptly
  • Manual test: start a Claude query, then Ctrl+C — should stop query and exit
  • Manual test: second Ctrl+C should force-exit immediately

🤖 Generated with Claude Code

The process was hanging on Ctrl+C because:
1. The rate-limiter's setInterval kept the event loop alive (no .unref())
2. No signal handlers existed to stop active Claude queries or disconnect Prisma

Fixes:
- Add .unref() to the rate-limiter cleanup interval
- Add SIGINT/SIGTERM handlers that stop all active Claude queries and
  disconnect Prisma before exiting
- A second Ctrl+C force-exits immediately

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces graceful shutdown handling by registering SIGINT and SIGTERM listeners to stop active Claude sessions and disconnect Prisma. It also applies .unref() to the rate limiter's cleanup interval to prevent it from hanging the process. A review comment highlights that the registration logic should be idempotent to prevent multiple listeners from being attached during Next.js development.

}

// Register graceful shutdown handler
registerShutdownHandler();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In Next.js, the register function in instrumentation.ts can be executed multiple times during development (e.g., due to Hot Module Replacement). This would result in multiple SIGINT/SIGTERM listeners being attached to the process, leading to redundant cleanup operations and potential race conditions during shutdown. It is recommended to ensure the signal handlers are only registered once by using a global flag, ensuring that initialization logic is idempotent and protected against concurrent or repeated execution.

    if (!(globalThis as any)._shutdownHandlerRegistered) {
      registerShutdownHandler();
      (globalThis as any)._shutdownHandlerRegistered = true;
    }
References
  1. To prevent race conditions in methods with asynchronous initialization, set a processing/locked flag synchronously at the start of the method, before any await calls, to ensure concurrent invocations are rejected immediately.

claude added 2 commits April 6, 2026 23:17
If stopAllSessions() or prisma.$disconnect() hangs, force exit after
10 seconds. This is especially important for SIGTERM from systemd
where there's no second signal to trigger force-exit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@brendanlong brendanlong merged commit 80b7577 into main Apr 7, 2026
4 checks passed
@brendanlong brendanlong deleted the claude/fbc11e47-5de2-480b-afe4-e98a54bcb3a6 branch April 7, 2026 06:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants