how to stop accepting new requests before closing existing ones?

177 Views Asked by At

This runs without error:

const app = express();

const server = app.listen(3900, '0.0.0.0', () => {
  console.log('listening...');
});

process.once('SIGINT', () => {

  console.log('got SIGINT.')

  setTimeout(() => {
    console.log('ending process due to SIGINT + subsequent Timeout.')
    process.exit(0);
  }, 3000);

  server.closeIdleConnections();  // <<<
  server.closeAllConnections();  // <<<<

  server.close(err => {
    if (err) {
      console.error(err);
    }
    console.log('closed server, and now shutting down due to SIGINT.')
    process.exit(0);
  });
});

but my question is - is this the best way to stop accepting new connections before ending the existing connections/requests?

how are "idle" connections defined?

3

There are 3 best solutions below

8
morganney On BEST ANSWER

is this the best way to stop accepting new connections before ending the existing connections/requests?

I guess "best" is subjective and it depends on the application. However, calling the synchronous server.closeAllConnections() before allowing the asynchronous server.close() to finish it's work is not the most graceful way to stop any ongoing connections (those currently sending a request or waiting for a response). In short, I would say no this is not the most graceful way to stop accepting new connections before ending existing connections due to the abrupt nature of closeAllConnections.

how are "idle" connections defined?

I'll link you to the documentation which states that idle connections are connections connected to this server which are not sending a request or waiting for a response.

To make your SIGINT handler more graceful, I would recommend a slight modification by removing the call to server.closeAllConnections() and nesting your setTimeout inside of the close callback. Something along these lines:

process.once('SIGINT', () => {
  /**
   * Immediately close any idle connections.
   * May not be strictly necessary since as of Node v19.0.0
   * server.close() will also close idle connections before
   * returning / firing the 'close' event.
   * 
   * Either way, it may reduce the workload of server.close()
   * by minimizing the connections it must close or wait to
   * finish to those that were already active, or only created
   * in the short time span between calling closeIdleConnections()
   * and close().
   */
  server.closeIdleConnections()

  // Now stop the server from accepting new connections while keeping existing connections.
  server.close((err) => {
    // Force close all connections after a specified timeout
    setTimeout(() => {
      server.closeAllConnections()
      process.exit(0)
    }, 3000).unref() // prevents the event loop from remaining active so your server can shutdown earlier if this timeout is not needed.
  })
})

Note, that the callback to server.close will only receive an error if you are attempting to shutdown a server that was not running. This is noted in the documentation for net.Server.close().

0
Alexander Mills On

In golang, we can do this:

server := &http.Server{
      // ...
}

listener, err := net.Listen("tcp", addr)

and then do this:

if err := listener.Close(); err != nil { // Stop listening for new connections
      // err
}
if err := server.Close(); err != nil { // Close all current/existing connections
       // err
}

the first call stops accepting new connections, the second call, closes all existing connections. It's nice to have them separate, but not sure how to do this with node.js

1
Evert On

but my question is - is this the best way to stop accepting new connections before ending the existing connections/requests?

You didn't quality 'best', but lets assume you want to gracefully shut down a HTTP server, without interrupting in-flight connections because new ones are spinning up.

closeAllConnections() will interupt any in-flight connections, so don't call that.

how are "idle" connections defined?

Idle connections are TCP connections that are opened by a HTTP client but aren't currently sending a HTTP request or waiting for a HTTP response. These are safe to close usually.