Skip to content

feat(backend): add graceful shutdown for HTTP and WebSocket servers (#170)#195

Merged
Penielka merged 1 commit into
AetherEdu:mainfrom
mattcrypted:feat/graceful-shutdown-170
Jun 28, 2026
Merged

feat(backend): add graceful shutdown for HTTP and WebSocket servers (#170)#195
Penielka merged 1 commit into
AetherEdu:mainfrom
mattcrypted:feat/graceful-shutdown-170

Conversation

@mattcrypted

Copy link
Copy Markdown
Contributor

Summary

Implements #170: graceful shutdown for the HTTP and WebSocket servers. On
SIGTERM or SIGINT the process stops accepting new traffic, drains in-flight
requests within a configurable grace period, closes the WebSocket and Redis
connections, and exits with code 0 on a clean drain or 1 on timeout.

Changes

  • New src/utils/shutdown.ts: a dependency-injected shutdown coordinator.
    • Runs ordered cleanup steps; a step that throws is logged and the rest still run.
    • Grace period via GRACEFUL_SHUTDOWN_TIMEOUT_MS (default 30000); the sequence is raced against this deadline so the process always exits within the grace window.
    • shutdownGuard middleware returns 503 for new requests during drain, exempting /api/health and / so orchestrators can still read state.
    • isShuttingDown() flag and closeHttpServer() helper (drains in-flight, frees idle keep-alive sockets).
  • src/index.ts: replaced the partial SIGINT-only stub with registerShutdownHandlers covering SIGTERM and SIGINT; registered steps for WebSocket close, HTTP drain, transaction queue/processor/events, and Redis; /api/health now returns 503 { status: 'shutting down' } during drain.
  • src/services/websocketService.ts: added close() which emits a server:shutdown notice to clients and force-disconnects sockets so the shared HTTP server can finish draining.
  • src/tests/shutdown.test.ts: 10 unit tests.

Definition of Done

  • SIGTERM and SIGINT handlers registered
  • HTTP server stops accepting new connections; new requests get 503
  • In-flight requests drain within a configurable grace period (default 30s)
  • Socket.IO closed with a "server shutting down" message
  • Redis closed gracefully (no Mongoose in this codebase; pg pools are created ad-hoc with no shared lifetime pool to close)
  • Health check reports "shutting down" during drain
  • Exit code 0 on clean shutdown, 1 on timeout

Testing

  • 10/10 unit tests pass; 100% line coverage of src/utils/shutdown.ts (98% statements).
  • Tests run with a setup-free jest config because main currently does not compile (pre-existing TypeScript errors in src/index.ts and other files, unrelated to this change; tests/setup.js imports src/index.ts). This change adds zero new tsc or eslint problems.

Closes #170

Handle SIGTERM and SIGINT by draining in-flight HTTP requests, closing the
WebSocket and Redis connections, and exiting with a code that reflects whether
the drain finished within the grace period.

- add src/utils/shutdown.ts: a dependency-injected shutdown coordinator with
  ordered cleanup steps, a grace period configurable via
  GRACEFUL_SHUTDOWN_TIMEOUT_MS (default 30s), exit 0 on a clean drain and 1 on
  timeout, a shutdownGuard middleware that returns 503 for new requests during
  drain, and an isShuttingDown flag
- wire the coordinator into src/index.ts, replacing the partial SIGINT stub;
  register WebSocket, HTTP drain, transaction worker, and Redis cleanup steps;
  report "shutting down" (503) from /api/health
- add WebsocketService.close(): notify clients with a "server shutting down"
  message and disconnect sockets so the shared HTTP server can finish draining
- add src/__tests__/shutdown.test.ts (10 tests, 100% line coverage of the
  new module)

Closes AetherEdu#170
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.

[Backend] Implement graceful shutdown handler for HTTP and WebSocket servers

2 participants