From 402ace512c1dc059081db7ee6f4dfc16eaef1478 Mon Sep 17 00:00:00 2001 From: Karim Jordan Date: Tue, 23 Apr 2024 05:28:15 +0700 Subject: [PATCH 01/13] push --- .../25-build-and-deploy/40-adapter-node.md | 32 +++++++++---------- packages/adapter-node/src/index.js | 12 +++++-- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/documentation/docs/25-build-and-deploy/40-adapter-node.md b/documentation/docs/25-build-and-deploy/40-adapter-node.md index 41be8063af1b..b0421fd059c5 100644 --- a/documentation/docs/25-build-and-deploy/40-adapter-node.md +++ b/documentation/docs/25-build-and-deploy/40-adapter-node.md @@ -186,6 +186,22 @@ By default `adapter-node` gracefully shuts down the HTTP server when a `SIGTERM` > If you want to customize this behaviour you can use a [custom server](#custom-server). +You can listen to the `sveltekit:shutdown` event which is emitted after the HTTP server has finished shutting down. Unlike Node's `exit` event, the `sveltekit:shutdown` event is always emitted when a shutdown is triggered and also supports handling asynchronous code. + +```js +process.on('sveltekit:shutdown', async (reason) => { + await db.close(); + await jobs.stop(); + process.exit(); +}); +``` + +The parameter `reason` has one of the following values: + +- `SIGINT` - shutdown was triggered by a `SIGINT` signal +- `SIGTERM` - shutdown was triggered by a `SIGTERM` signal +- `IDLE` - shutdown was triggered by [`IDLE_TIMEOUT`](#environment-variables-idle-timeout) + ## Socket activation Most Linux operating systems today use a modern process manager called systemd to start the server and run and manage services. You can configure your server to allocate a socket and start and scale your app on demand. This is called [socket activation](http://0pointer.de/blog/projects/socket-activated-containers.html). In this case, the OS will pass two environment variables to your app — `LISTEN_PID` and `LISTEN_FDS`. The adapter will then listen on file descriptor 3 which refers to a systemd socket unit that you will have to create. @@ -242,19 +258,3 @@ app.listen(3000, () => { console.log('listening on port 3000'); }); ``` - -## Troubleshooting - -### Is there a hook for cleaning up before the app exits? - -There's nothing built-in to SvelteKit for this, because such a cleanup hook depends highly on the execution environment you're on. For Node, you can use its built-in `process.on(...)` to implement a callback that runs before the app exits: - -```js -// @errors: 2304 2580 -function shutdownGracefully() { - // anything you need to clean up manually goes in here - db.shutdown(); -} - -process.on('exit', shutdownGracefully); -``` diff --git a/packages/adapter-node/src/index.js b/packages/adapter-node/src/index.js index 929610362166..f7f2a152ff2f 100644 --- a/packages/adapter-node/src/index.js +++ b/packages/adapter-node/src/index.js @@ -42,19 +42,25 @@ if (socket_activation) { }); } -function shutdown() { +/** @param {'SIGINT' | 'SIGTERM' | 'IDLE'} reason */ +function shutdown(reason) { if (shutdown_timeout_id) return; // @ts-expect-error this was added in 18.2.0 but is not reflected in the types server.server.closeIdleConnections(); - server.server.close(() => { + server.server.close((error) => { + if (error) return + if (shutdown_timeout_id) { shutdown_timeout_id = clearTimeout(shutdown_timeout_id); } if (idle_timeout_id) { idle_timeout_id = clearTimeout(idle_timeout_id); } + + // @ts-expect-error Custom events cannot be typed + process.emit('sveltekit:shutdown', reason); }); shutdown_timeout_id = setTimeout( @@ -83,7 +89,7 @@ server.server.on( server.server.closeIdleConnections(); } if (requests === 0 && socket_activation && idle_timeout) { - idle_timeout_id = setTimeout(shutdown, idle_timeout * 1000); + idle_timeout_id = setTimeout(() => shutdown('IDLE'), idle_timeout * 1000); } }); } From 716e2fd3b6af4317bd03e1ad4186fcec4b260cb6 Mon Sep 17 00:00:00 2001 From: Karim Jordan Date: Tue, 23 Apr 2024 05:54:16 +0700 Subject: [PATCH 02/13] add changeset --- .changeset/cuddly-otters-repeat.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/cuddly-otters-repeat.md diff --git a/.changeset/cuddly-otters-repeat.md b/.changeset/cuddly-otters-repeat.md new file mode 100644 index 000000000000..a38427a0cb8a --- /dev/null +++ b/.changeset/cuddly-otters-repeat.md @@ -0,0 +1,5 @@ +--- +"@sveltejs/adapter-node": minor +--- + +add shutdown event From e7b5a5a896d239ccdd9be5907707b3ebc87ddbd2 Mon Sep 17 00:00:00 2001 From: Karim Jordan Date: Wed, 12 Jun 2024 00:05:56 +0700 Subject: [PATCH 03/13] format --- packages/adapter-node/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/adapter-node/src/index.js b/packages/adapter-node/src/index.js index f7f2a152ff2f..da4d86d71d1e 100644 --- a/packages/adapter-node/src/index.js +++ b/packages/adapter-node/src/index.js @@ -50,7 +50,7 @@ function shutdown(reason) { server.server.closeIdleConnections(); server.server.close((error) => { - if (error) return + if (error) return; if (shutdown_timeout_id) { shutdown_timeout_id = clearTimeout(shutdown_timeout_id); From 6ecee78acb9125f6b9161536cfa119762efd5196 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Wed, 12 Jun 2024 20:21:41 -0700 Subject: [PATCH 04/13] Update .changeset/cuddly-otters-repeat.md --- .changeset/cuddly-otters-repeat.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/cuddly-otters-repeat.md b/.changeset/cuddly-otters-repeat.md index a38427a0cb8a..83c94f362189 100644 --- a/.changeset/cuddly-otters-repeat.md +++ b/.changeset/cuddly-otters-repeat.md @@ -2,4 +2,4 @@ "@sveltejs/adapter-node": minor --- -add shutdown event +feat: add shutdown event From 592999dc5f5f5784562abc5b8372ba86a8905ad1 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Wed, 12 Jun 2024 21:01:04 -0700 Subject: [PATCH 05/13] Update packages/adapter-node/src/index.js --- packages/adapter-node/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/adapter-node/src/index.js b/packages/adapter-node/src/index.js index da4d86d71d1e..293cb109b93d 100644 --- a/packages/adapter-node/src/index.js +++ b/packages/adapter-node/src/index.js @@ -59,7 +59,7 @@ function shutdown(reason) { idle_timeout_id = clearTimeout(idle_timeout_id); } - // @ts-expect-error Custom events cannot be typed + // @ts-expect-error custom events cannot be typed process.emit('sveltekit:shutdown', reason); }); From f361ac57d804fb6337cd824de1db2e5ba382c552 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Wed, 12 Jun 2024 21:08:05 -0700 Subject: [PATCH 06/13] Update packages/adapter-node/src/index.js --- packages/adapter-node/src/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/adapter-node/src/index.js b/packages/adapter-node/src/index.js index 293cb109b93d..5a9c82587dc3 100644 --- a/packages/adapter-node/src/index.js +++ b/packages/adapter-node/src/index.js @@ -50,6 +50,7 @@ function shutdown(reason) { server.server.closeIdleConnections(); server.server.close((error) => { + // occurs if the server is already closed if (error) return; if (shutdown_timeout_id) { From d1b73ebd586d83fb76ce5af74272c756a2e0712c Mon Sep 17 00:00:00 2001 From: Karim Jordan <113807903+karimfromjordan@users.noreply.github.com> Date: Thu, 13 Jun 2024 12:37:59 +0700 Subject: [PATCH 07/13] Update documentation/docs/25-build-and-deploy/40-adapter-node.md Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- documentation/docs/25-build-and-deploy/40-adapter-node.md | 1 - 1 file changed, 1 deletion(-) diff --git a/documentation/docs/25-build-and-deploy/40-adapter-node.md b/documentation/docs/25-build-and-deploy/40-adapter-node.md index b0421fd059c5..528747910a76 100644 --- a/documentation/docs/25-build-and-deploy/40-adapter-node.md +++ b/documentation/docs/25-build-and-deploy/40-adapter-node.md @@ -192,7 +192,6 @@ You can listen to the `sveltekit:shutdown` event which is emitted after the HTTP process.on('sveltekit:shutdown', async (reason) => { await db.close(); await jobs.stop(); - process.exit(); }); ``` From 1bf473fe1666ecbdfa17fe5d8be43d997b231c87 Mon Sep 17 00:00:00 2001 From: Karim Jordan <113807903+karimfromjordan@users.noreply.github.com> Date: Fri, 14 Jun 2024 01:22:38 +0700 Subject: [PATCH 08/13] Update documentation/docs/25-build-and-deploy/40-adapter-node.md Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- documentation/docs/25-build-and-deploy/40-adapter-node.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/25-build-and-deploy/40-adapter-node.md b/documentation/docs/25-build-and-deploy/40-adapter-node.md index 528747910a76..b2b66ea9019f 100644 --- a/documentation/docs/25-build-and-deploy/40-adapter-node.md +++ b/documentation/docs/25-build-and-deploy/40-adapter-node.md @@ -186,7 +186,7 @@ By default `adapter-node` gracefully shuts down the HTTP server when a `SIGTERM` > If you want to customize this behaviour you can use a [custom server](#custom-server). -You can listen to the `sveltekit:shutdown` event which is emitted after the HTTP server has finished shutting down. Unlike Node's `exit` event, the `sveltekit:shutdown` event is always emitted when a shutdown is triggered and also supports handling asynchronous code. +You can listen to the `sveltekit:shutdown` event which is emitted after the HTTP server has closed all connections. Unlike Node's `exit` event, the `sveltekit:shutdown` event supports asynchronous operations and is always emitted when all connections are closed even if the server has dangling work such as open database connections. ```js process.on('sveltekit:shutdown', async (reason) => { From 9f6d6c1eee69717aac5586a95f077dc3b75e791c Mon Sep 17 00:00:00 2001 From: Karim Jordan Date: Fri, 14 Jun 2024 01:45:38 +0700 Subject: [PATCH 09/13] rename `shutdown` to `graceful_shutdown` --- packages/adapter-node/src/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/adapter-node/src/index.js b/packages/adapter-node/src/index.js index 5a9c82587dc3..2bf14f919851 100644 --- a/packages/adapter-node/src/index.js +++ b/packages/adapter-node/src/index.js @@ -43,7 +43,7 @@ if (socket_activation) { } /** @param {'SIGINT' | 'SIGTERM' | 'IDLE'} reason */ -function shutdown(reason) { +function graceful_shutdown(reason) { if (shutdown_timeout_id) return; // @ts-expect-error this was added in 18.2.0 but is not reflected in the types @@ -90,13 +90,13 @@ server.server.on( server.server.closeIdleConnections(); } if (requests === 0 && socket_activation && idle_timeout) { - idle_timeout_id = setTimeout(() => shutdown('IDLE'), idle_timeout * 1000); + idle_timeout_id = setTimeout(() => graceful_shutdown('IDLE'), idle_timeout * 1000); } }); } ); -process.on('SIGTERM', shutdown); -process.on('SIGINT', shutdown); +process.on('SIGTERM', graceful_shutdown); +process.on('SIGINT', graceful_shutdown); export { server }; From 36b43d70ade2aee76008d1c643926d2664ba5add Mon Sep 17 00:00:00 2001 From: Karim Jordan Date: Fri, 14 Jun 2024 01:50:06 +0700 Subject: [PATCH 10/13] close idle connections immediately --- packages/adapter-node/src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/adapter-node/src/index.js b/packages/adapter-node/src/index.js index 2bf14f919851..71734149eac7 100644 --- a/packages/adapter-node/src/index.js +++ b/packages/adapter-node/src/index.js @@ -84,8 +84,8 @@ server.server.on( req.on('close', () => { requests--; - if (requests === 0 && shutdown_timeout_id) { - // when all requests are done, close the connections, so the app shuts down without delay + if (shutdown_timeout_id) { + // close connections as soon as they become idle, so the app shuts down without delay // @ts-expect-error this was added in 18.2.0 but is not reflected in the types server.server.closeIdleConnections(); } From ea13e16943520e2e26d55200ffd08fa555f31f43 Mon Sep 17 00:00:00 2001 From: Karim Jordan Date: Fri, 14 Jun 2024 01:53:15 +0700 Subject: [PATCH 11/13] update docs --- documentation/docs/25-build-and-deploy/40-adapter-node.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/25-build-and-deploy/40-adapter-node.md b/documentation/docs/25-build-and-deploy/40-adapter-node.md index b2b66ea9019f..b08e5505faed 100644 --- a/documentation/docs/25-build-and-deploy/40-adapter-node.md +++ b/documentation/docs/25-build-and-deploy/40-adapter-node.md @@ -190,8 +190,8 @@ You can listen to the `sveltekit:shutdown` event which is emitted after the HTTP ```js process.on('sveltekit:shutdown', async (reason) => { - await db.close(); await jobs.stop(); + await db.close(); }); ``` From ee8b2d559ead434ba1faad8f12a21803392ae970 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Thu, 13 Jun 2024 14:09:32 -0700 Subject: [PATCH 12/13] code comments --- packages/adapter-node/src/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/adapter-node/src/index.js b/packages/adapter-node/src/index.js index 71734149eac7..423bd634f296 100644 --- a/packages/adapter-node/src/index.js +++ b/packages/adapter-node/src/index.js @@ -46,6 +46,8 @@ if (socket_activation) { function graceful_shutdown(reason) { if (shutdown_timeout_id) return; + // If a connection was opened with a keep-alive header close() will wait for the connection to + // time out rather than close it even if it is not handling any requests, so call this first // @ts-expect-error this was added in 18.2.0 but is not reflected in the types server.server.closeIdleConnections(); @@ -85,7 +87,7 @@ server.server.on( requests--; if (shutdown_timeout_id) { - // close connections as soon as they become idle, so the app shuts down without delay + // close connections as soon as they become idle, so they don't accept new requests // @ts-expect-error this was added in 18.2.0 but is not reflected in the types server.server.closeIdleConnections(); } From bf753227d3e33df2cec86734eaed5c703e27877f Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Thu, 13 Jun 2024 14:13:28 -0700 Subject: [PATCH 13/13] Create gold-turkeys-pump.md --- .changeset/gold-turkeys-pump.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/gold-turkeys-pump.md diff --git a/.changeset/gold-turkeys-pump.md b/.changeset/gold-turkeys-pump.md new file mode 100644 index 000000000000..5dc24388408d --- /dev/null +++ b/.changeset/gold-turkeys-pump.md @@ -0,0 +1,5 @@ +--- +"@sveltejs/adapter-node": patch +--- + +fix: close keep-alive connections as soon as possible during graceful shutdown rather than accepting new requests