Skip to content

[runtime] Make SIGTERM/SIGINT close the Node listener and handle shutdown failure #1684

Description

@kang-heewon

Priority

P1

Problem

The shutdown signal callback returns an unobserved Promise, so timeout failures become unhandled rejections; separately, the Node listen path does not bind its server close lifecycle to graceful shutdown. A terminated process can retain its listening socket.

Evidence

packages/framework-context/src/libs/ShutdownManager.ts registers async handleSignal directly with process.on, while its timeout path rejects. packages/transports-http/src/libs/CrocoApp.ts starts the server without a shutdown owner; GracefulShutdownMiddleware.ts drains requests but does not close that server.

Desired outcome

A signal produces one observable shutdown outcome, stops accepting connections, drains bounded work, closes the Node listener, and exits according to a documented failure policy.

Implementation path

  • Wrap signal shutdown with explicit catch/log/exit policy.
  • Make listen() expose or own a closeable server lifecycle.
  • Order reject-new-work, drain, close, hooks, and telemetry flush deterministically.

Acceptance criteria

  • A hanging hook does not create an unhandled rejection.
  • SIGTERM closes the listener after the configured drain.
  • Repeated signals are idempotent.

Validation

  • Add real Node socket tests for SIGTERM, keep-alive drain, timeout, and listener close.
  • Run pnpm --filter @croco/framework-context test and pnpm --filter @croco/transports-http test.

Scope

Does not change serverless shutdown semantics.

Metadata

Metadata

Assignees

Labels

P1Priority 1 issuebugSomething isn't workingreliabilityruntimeRuntime behavior, lifecycle, and environment capability work

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions