Search code examples
javascriptnode.jses6-promise

Do I need to restart my application after unhandledRejection


I have a nodeJS application (server) and using some 3rd party npm modules. Also in my application, I have the following code:

process.on("unhandledRejection", (reason, promise) => {
    console.error(`Unhandled Rejection at: ${promise} reason: ${reason}`);
    restartApp(); // ← Is this a must action?
});

Seems like not all the promises are rejected properly, in the 3rd party modules and maybe also in my code. I know it's the last resource to use this event handler.

Question after catching this unhandle rejection event, do I need to restart my application?


Solution

  • It's useful to divide errors into two broad categories: Operational and Programmer errors.

    Operational errors are (unavoidable) run-time problems experienced by correctly-written programs, such as disk full, network connection loss etc.

    Programmer errors are caused by bugs or oversights in code, that can not be handled since they will cause the program to enter an unknown state, they could be in your code or a module you're calling.

    Best practice for programmer errors is to crash immediately. You should run your programs using a restarter (see below) that will automatically restart the program in the event of a crash. With a restarter in place, crashing is the fastest way to restore reliable service in the face of a transient programmer error.

    If the error is an operational error, it may make sense to try to recover or re-try the operation (if this makes sense). There's no point retrying a REST request if you're getting 400 errors for example, but there might be if you're getting 500 errors (the system may recover).

    See more here in this very useful guide: https://www.joyent.com/node-js/production/design/errors

    In your specific case, you're handling an unhandledRejection, this means that an application is in an undefined state... very similar to an unhandledException, the best thing to do is clean up anything that needs to be done and then exit or restart, also log the error (this is super important!, is the error happening every day, hour or minute?)

    I'd suggest using a process monitor such as PM2 or Forever. These can auto-restart when you exit due to an error, and do lots of other cool stuff like logging these events.

    Here's another nice guide from Heroku on the same topic: https://blog.heroku.com/best-practices-nodejs-errors

    The blogger (Julián Duque) has even put together some best practice on handling these events: https://blog.heroku.com/best-practices-nodejs-errors#putting-it-all-together

    This looks a little like so:

    const http = require('http')
    const terminate = require('./terminate')
    const server = http.createServer(...)
    
    const exitHandler = terminate(server, {
      coredump: false,
      timeout: 500
    })
    
    process.on('uncaughtException', exitHandler(1, 'Unexpected Error'))
    process.on('unhandledRejection', exitHandler(1, 'Unhandled Promise'))
    process.on('SIGTERM', exitHandler(0, 'SIGTERM'))
    process.on('SIGINT', exitHandler(0, 'SIGINT'))
    

    The Terminate module:

    function terminate (server, options = { coredump: false, timeout: 500 })    {
    
      return (code, reason) => (err, promise) => {
    
        // Exit function
        const exit = () => {
          options.coredump ? process.abort() : process.exit(code)
        }
    
        if (err && err instanceof Error) {
        // Log error information, use a proper logging library here :)
        console.log(err.message, err.stack)
        }
    
        // Attempt a graceful shutdown
        server.close(exit)
        setTimeout(exit, options.timeout).unref()
      }
    }
    
    module.exports = terminate
    

    I think this style of managed, centralized handling of these events is the right way to go.