Shutting down the application
Up until now, we have killed our server by pressing the Ctrl + C or Cmd+ C keys. This shortcut sends a SIGINT
interrupt to the Node.js process, which will cause an unconditional termination. If we don’t manage this behavior, a running HTTP request may be interrupted, causing possible data loss or introducing inconsistencies in our application.
To ensure you close the server gracefully, you must call the root instance’s close
method:
process.once('SIGINT', function closeApplication() { app.close(function closeComplete(err) { if (err) { app.log.error(err, 'error turning off') } else { app.log.info('bye bye') } }) })
Adding this signaling handle will prevent the kill of the server, thus allowing the complete execution of the requests and preventing new HTTP requests from being accepted. New requests will receive the HTTP Status 503 - Service Unavailable
error while the application is in the closing phase.
Calling the close
method will trigger the onClose
hook too. All the plugins that are listening for this event will receive it at the beginning of the shutdown process, as a database plugin will close the connection.
Fastify guarantees that the onClose
hooks will be executed once, even when the server’s close
method is called multiple times. Note that the close
callback function will be run at every call instead.
Our implementation, unfortunately, is not enough to cover all the use cases one application may face. If the plugins don’t resolve the onClose
hook, due to a bug or a starving connection, our server will become a zombie that will wait forever to close gracefully. For this reason, we need to develop a maximum time span, after which the application must stop. So, let’s analyze an example of force close using the Fastify async interface:
process.once('SIGINT', async function closeApplication() { const tenSeconds = 10_000 const timeout = setTimeout(function forceClose() { app.log.error('force closing server') process.exit(1) }, tenSeconds) timeout.unref() try { await app.close() app.log.info('bye bye') } catch (err) { app.log.error(err, 'the app had trouble turning off') } })
We have set a timeout timer in the previous code that doesn’t keep the Node.js event loop active, thanks to the unref
call. If the close callback doesn’t execute in 10 seconds, the process will exit with a nonzero result. This pattern is implemented in many plugins built by Fastify’s community that you can check out on the Ecosystem page at https://www.fastify.io/ecosystem/.
Turning off a server could be challenging, but Fastify provides a set of features that help us to avoid losing data and complete all the application’s pending tasks. We have seen how to deal with it through a pattern that guarantees to stop the server in a reasonable time. Looking at the community plugins is a good way to learn how to search for an external plugin that implements the pattern and provides us with this feature, without having to re-implement it ourselves.