Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions server/services/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -923,14 +923,15 @@ const withGrace = (label, ms, run) => new Promise((resolve) => {
setTimeout(() => finishWithError(`⚠️ ${label} close exceeded ${ms}ms — proceeding`), ms).unref?.();
});

// graceMs is deliberately short: closeAllConnections() force-drops every
// connection, so there is no graceful drain left to wait for — the only thing that
// can outlast it is a WebSocket-upgraded socket the server no longer tracks (and
// graceMs is deliberately bounded: closeAllConnections() force-drops every
// connection, so there is no graceful drain left to wait for. The close callback
// still needs a short window for TLS/socket teardown — the only thing that can
// outlast it is a WebSocket-upgraded socket the server no longer tracks (and
// io.close()'s engine.close() already tore those down protocol-side; the OS reaps
// the TCP remnant on process.exit). So don't tax every restart waiting on it.
// the TCP remnant on process.exit).
// ERR_SERVER_NOT_RUNNING means it was already closed (io.close() closes whichever
// server is its current this.httpServer) — success for us, not a failure.
const closeServer = (server, label, graceMs = 250) => withGrace(label, graceMs, ({ finish, finishWithError }) => {
const closeServer = (server, label, graceMs = 1000) => withGrace(label, graceMs, ({ finish, finishWithError }) => {
if (!server) return finish();
server.close((err) => {
if (err && err.code !== 'ERR_SERVER_NOT_RUNNING') finishWithError(`⚠️ Error closing ${label}`, err);
Expand Down
4 changes: 2 additions & 2 deletions server/services/bootstrap.shutdown.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,12 @@ describe('bounded server shutdown', () => {
let onClose;
const settled = vi.fn();
const closing = closeServer({ close: (done) => { onClose = done; } }, 'HTTP server').then(settled);
await vi.advanceTimersByTimeAsync(249);
await vi.advanceTimersByTimeAsync(999);
expect(settled).not.toHaveBeenCalled();
await vi.advanceTimersByTimeAsync(1);
await closing;
expect(settled).toHaveBeenCalledTimes(1);
expect(error.mock.calls).toEqual([['⚠️ HTTP server close exceeded 250ms — proceeding']]);
expect(error.mock.calls).toEqual([['⚠️ HTTP server close exceeded 1000ms — proceeding']]);
onClose();
onClose(new Error('late failure'));
expect(log).not.toHaveBeenCalled();
Expand Down