kit icon indicating copy to clipboard operation
kit copied to clipboard

Hook for cleaning up server?

Open IRod22 opened this issue 3 years ago • 12 comments

Describe the problem

Issue #1538 discusses about setting up external resources, but how do you clean them up?

Describe the proposed solution

The solution would be a cleanup() hook. Here is an example:

import DB from '$lib/db
const db = new DB(...)
const setup = db.connect() // returns a promise

/** @type {import('@sveltejs/kit').[Handle](https://kit.svelte.dev/docs/types#sveltejs-kit-handle)} */
export const handle = async ({ event, resolve }) => {
    await setup
    event.locals.db = db
    return await resolve(event)
}

export const cleanup = async () => {
    await db.disconnect()
}

Alternatives considered

Not Applicable

Importance

i cannot use SvelteKit without it

Additional Information

I chose the "i cannot use SvelteKit without it" because releasing resources is very important in any server application.

IRod22 avatar Sep 15 '22 20:09 IRod22

I'm going to assume you're specifically talking about apps using the Node adapter.

Releasing resources when what? When the server process terminates? What sorts of resources are you concerned about not being automatically released already when the process terminates?

There's also currently not a reason (other than a catastrophic runtime error) that would result in the Node process from terminating besides it getting sent something like SIGINT, so you could also listen to that signal.

Conduitry avatar Sep 15 '22 20:09 Conduitry

I meant to say that there should be a hook for releasing resources when the process is about to terminate via Ctrl-C or when using HMR. I am using the auto adapter that was configured by create-svelte.

ghost avatar Sep 15 '22 21:09 ghost

Again: What sorts of resources?

And are you deploying a production build anywhere? If you're just using npm run dev (or npm run preview) locally, then what adapter you have selected doesn't matter, because the adapter isn't used.

What does it mean to clean up resources when you're deploying to some sort of serverless environment? I don't see how this can be cross-platform. If you are targeting just Node, then you can use process.on('exit', ...), process.on('SIGINT', ...) etc as appropriate, but I don't see what the framework can provide.

Conduitry avatar Sep 15 '22 21:09 Conduitry

The resources are anything that need to be awaited to close before the process is terminated. The example code shows a database connection (it could be knex, typeorm, etc.) with the connect and disconnect methods returning promises to be awaited by the hooks.

ghost avatar Sep 15 '22 22:09 ghost

Never mind. I'm using a query builder that automatically disconnects when the process exits. :man_facepalming: However, some devs may need to manually close a database or Redis connection when the process exits (see the sqlite3 API for example), so this still may need discussion either way.

ghost avatar Sep 18 '22 02:09 ghost

For clarity, there's no opportunity to clean up resources when a module is invalidated during development. This is an issue with Vite: https://github.com/vitejs/vite/issues/7887

Rich-Harris avatar Sep 19 '22 21:09 Rich-Harris

I had a similar question about a prod sveltekit app. I am using adapter-node. I use mysql connection pools and on shutdown need to call pool.end() otherwise I have found the database can hang. The cases it would usually hang are definitely due to bugs in my app, but shutting down gracefully makes sure my bugs don't hang the database.

Before migrating to sveltekit I would wait for the http server to close before calling pool.end(). e.g., process.on('SIGINT', () => server.close(() => pool.end())); Using adapter-node, I have not figured out a way to achieve the same thing.

It might be safe for me to call pool.end() with out waiting for the server to close. The app is shutting down anyway, so a potential brief window of the server running but the pool being closed is probably not an issue.

tsmigiel avatar Oct 11 '22 19:10 tsmigiel

To follow up on my example. The solution I am currently using is to call process.on('exit', ...) and process.on('SIGINT', ...) to call my clean up code. I am still a beginner, so if this is a poor approach, I appreciate any feedback.

I wrote a few details here: https://stackoverflow.com/questions/74020726/how-to-shutdown-gracefully-in-sveltekit

tsmigiel avatar Oct 15 '22 01:10 tsmigiel

Using containerized sveltekit with adapter-node this works for me

Containerfile/Dockerfile (the end of it)

COPY --from=build /app/build build
ADD entry.js /app

EXPOSE 3000
ENTRYPOINT ["node", "/app/entry.js"]

entry.js

import {server as app} from "./build/index.js"

function shutdownGracefully() {
    console.log("Server doing graceful shutdown");
    app.server.close();
}

process.on("SIGINT", shutdownGracefully);
process.on("SIGTERM", shutdownGracefully);

podman run ... output after podman stop ...

Listening on 0.0.0.0:3000 Server doing graceful shutdown

konstantinblaesi avatar Nov 29 '22 12:11 konstantinblaesi

@konstantinblaesi, although I appreciate your suggestion, Podman requires WSL2 (I have a Windows machine unfortunately :man_facepalming:), which requires a paid license, and Docker does require a paid subscription and may also need WSL2 as well.

Although there are other options, with the knowledge gained from being an activist, I am probably speaking for other Svelte devs here by saying the following: with the current crises going on messing with the global economy, the most astute of us, including me, are trying to manage our money. Some devs cannot risk letting the price of one or more software licenses and subscriptions eating up their budgets, especially if the economy is poised to worsen in a few months.

That reason is why I think other devs and Sveltekit newcomers are looking for a built-in alternatives for an open source project, and not have to find out they need a paid subscription just to use the software they need while they are on a budget.

ghost avatar Nov 29 '22 14:11 ghost

@IRod22 sorry to disappoint you but your development setup is not my concern, there are free and non free options and also I don't make any proposals here what the node adapter should or should not implement . Make your choice / pick your poison I guess :D

I provide a solution that is usable today given someone wants to put their sveltekit app in a nodejs container. I use podman on wsl2 fedora at work and podman on bare metal fedora at home. Me using wsl2 for work is just a consequence of my IT providing this by default and me not taking the time yet to mess around trying to change it to bare metal linux which I use on all my private machines.

Podman by the way is an open source tool developed by Red Hat (now IBM) and completely free, just like most of the tooling for making containers. Docker just made containers more accesible, improved the developer experience, provides more convenience. I don't use docker anywhere today, just open source / free tooling.

You might want to read some about operating system processes and signals because SIGINT and SIGTERM are such signals and two common ways an operating system might tell a process to shutdown gracefully. That's what my entry.js listens for and when containers are told to stop they send a SIGTERM to your process, if the application does not shutdown it gets killed (signal = SIGKILL) and has no chance to do cleanup, it just dies. When using docker/podman your process receives a SIGTERM and has 10 seconds to shutdown by default, after that it will be killed with SIGKILL.

If you don't need a container you may just use the entry.js for graceful shutodwn, but I assume you'd rather see this officially supported which makes sense to me, too.

konstantinblaesi avatar Nov 29 '22 15:11 konstantinblaesi

@IRod22 sorry to disappoint you but your development setup is not my concern, . . . I know that, @konstantinblaesi. I'm just stating a fact.

ghost avatar Nov 29 '22 20:11 ghost

As Conduitry pointed out, there's no cross-platform way to achieve this, so using the native functionality of the environment you're in is the recommended solution. For Node that's the mentioned process.on('SIGINT', ...)

dummdidumm avatar Jan 11 '23 10:01 dummdidumm