hemerajs / fastify-graceful-shutdown

Gracefully shutdown fastify

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Graceful shutdown only works if fastify server started and terminates without processing any requests

ctv-bigi opened this issue · comments

When testing this plugin, I find that it only works (fastify v4) when I start the server, then ctrl-c immediately to shut it down. It gracefully shuts down correctly and runs any onClose hooks I have.

If I start the server, make a request hit to a route handler, and then ctrl-c to shut it down, it gets stuck and always waits for the 10s timeout.

This happens even if that route handler does nothing at all.

Hi @ctv-bigi thank you for reporting the issue. I was able to reproduce it. You can fix it by listening on ipv4 address 127.0.0.1 instead of localhost which listen to dual stack on most systems.

@StarpTech Hi, i has the same probrem

  server.register(require('fastify-graceful-shutdown'))
    .after(() => {
      server.gracefulShutdown(async (signal, next) => {
        console.log(`Upps! ${signal}`)
        await server.close()
        console.log(`server closed`)
        next()
      })
    })

    server.route({
      url: "/",
      method: "GET",
      handler: async function (request, reply) {
        console.log('start wait timeout');
        await new Promise<void>((RES) => {
          setTimeout(() => {
            RES()
          }, 5000);
        })
        console.log('5s end');
        return {
          status: "up",
        };
      },
    });

  server.listen(
    {
      host: "0.0.0.0",
      port: 3000,
    },
    (err) => {
      if (err) {
        console.error(err);
        process.exit(1);
      }
    }
  );

I run node app.js and ctrl+c shutdown, the log like this.

0s start wait timeout
3s Upps! SIGINT
5s end
10s [2024-03-23 21:04:53.661 +0800] ERROR: terminate process after timeout {"plugin":"fastify-graceful-shutdown","signal":"SIGINT","timeout":10000}

Why not stop immediately after the route return of 5s?

Hi, I can't reproduce it. You should not close the server yourself. The plugin will call it after all other handlers has been called.

'use strict'

const fastify = require('fastify')({
  logger: {
    level: 'info',
  },
})

fastify.register(require('./')).after((err) => {
  fastify.log.error(err)
  // Register custom clean up handler
  fastify.gracefulShutdown((code, cb) => {
    fastify.log.info('Graceful shutdown ...')
    cb()
    fastify.log.info('Graceful shutdown complete')
  })
})

const schema = {
  schema: {
    response: {
      200: {
        type: 'object',
        properties: {
          hello: {
            type: 'string',
          },
        },
      },
    },
  },
}

fastify.get('/', schema, async function (req, reply) {
  await new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve()
    }, 5000)
  })
  reply.send({ hello: 'world' })
})

fastify.listen(
  {
    host: '127.0.0.1',
    port: 3000,
  },
  (err) => {
    if (err) throw err
    console.log(`server listening on ${fastify.server.address().port}`)
  },
)
{"level":50,"time":1711214092034,"pid":248845,"hostname":"pop-os","msg":null}
server listening on 3000
{"level":30,"time":1711214092040,"pid":248845,"hostname":"pop-os","msg":"Server listening at http://127.0.0.1:3000"}
{"level":30,"time":1711214099827,"pid":248845,"hostname":"pop-os","reqId":"req-1","req":{"method":"GET","url":"/","hostname":"localhost:3000","remoteAddress":"127.0.0.1","remotePort":53902},"msg":"incoming request"}
^C{"level":30,"time":1711214101301,"pid":248845,"hostname":"pop-os","msg":"Graceful shutdown ..."}
{"level":30,"time":1711214101301,"pid":248845,"hostname":"pop-os","msg":"Graceful shutdown complete"}
{"level":30,"time":1711214104831,"pid":248845,"hostname":"pop-os","reqId":"req-1","res":{"statusCode":200},"responseTime":5003.497728999704,"msg":"request completed"}
{"level":30,"time":1711214104894,"pid":248845,"hostname":"pop-os","reqId":"req-2","res":{"statusCode":503},"msg":"request aborted - refusing to accept new requests as server is closing"}

@StarpTech Hi, please try this

'use strict'

const fastify = require('fastify')({
    logger: {
        level: 'info',
    },
})

fastify.register(require('fastify-graceful-shutdown')).after((err) => {
    fastify.log.error(err)
    // Register custom clean up handler
    fastify.gracefulShutdown((code, cb) => {
        fastify.log.info('Graceful shutdown ...')
        cb()
        fastify.log.info('Graceful shutdown complete')
    })
})


fastify.get('/', async function (req, reply) {
    console.log('start');
    await new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve()
        }, 5000)
    })
    console.log('end');
    reply.send({ hello: 'world' })
})

fastify.listen(
    {
        host: '127.0.0.1',
        port: 3000,
    }
)

shell

$ node app.js
{"level":50,"time":1711259359199,"pid":100903,"hostname":"ubuntu-server","msg":null}
{"level":30,"time":1711259359201,"pid":100903,"hostname":"ubuntu-server","msg":"Server listening at http://127.0.0.1:3000"}
{"level":30,"time":1711259361030,"pid":100903,"hostname":"ubuntu-server","reqId":"req-1","req":{"method":"GET","url":"/?vscodeBrowserReqId=1711259360797","hostname":"localhost:3000","remoteAddress":"127.0.0.1","remotePort":36048},"msg":"incoming request"}
start
^C{"level":30,"time":1711259362266,"pid":100903,"hostname":"ubuntu-server","msg":"Graceful shutdown ..."}
{"level":30,"time":1711259362266,"pid":100903,"hostname":"ubuntu-server","msg":"Graceful shutdown complete"}
end
{"level":30,"time":1711259366037,"pid":100903,"hostname":"ubuntu-server","reqId":"req-1","res":{"statusCode":200},"responseTime":5006.029588997364,"msg":"request completed"}
{"level":50,"time":1711259372270,"pid":100903,"hostname":"ubuntu-server","plugin":"fastify-graceful-shutdown","signal":"SIGINT","timeout":10000,"msg":"terminate process after timeout"}

What Node.js, Fastify versioning are you using?

What Node.js, Fastify versioning are you using?

  • node: v21.7.1
  • fastify: 4.26.2

I use Node 20. What is your operating system? Those are my logs from your example after a single request.

{"level":50,"time":1711276410599,"pid":19248,"hostname":"pop-os","msg":null}
{"level":30,"time":1711276410601,"pid":19248,"hostname":"pop-os","msg":"Server listening at http://127.0.0.1:3000"}
{"level":30,"time":1711276415582,"pid":19248,"hostname":"pop-os","reqId":"req-1","req":{"method":"GET","url":"/","hostname":"localhost:3000","remoteAddress":"127.0.0.1","remotePort":49580},"msg":"incoming request"}
start
^C{"level":30,"time":1711276417053,"pid":19248,"hostname":"pop-os","msg":"Graceful shutdown ..."}
{"level":30,"time":1711276417053,"pid":19248,"hostname":"pop-os","msg":"Graceful shutdown complete"}
end
{"level":30,"time":1711276420586,"pid":19248,"hostname":"pop-os","reqId":"req-1","res":{"statusCode":200},"responseTime":5004.05306299997,"msg":"request completed"}
{"level":30,"time":1711276420648,"pid":19248,"hostname":"pop-os","reqId":"req-2","res":{"statusCode":503},"msg":"request aborted - refusing to accept new requests as server is closing"}

I use ubuntu22.04, thanks you, i may know the problem

It can be work use curl

  1. node app.js
  2. curl http://127.0.0.1:3000
  3. ctrl+c
  4. it wait the response return and exit service

And use chrome, it terminate process after timeout...

  1. node app.js
  2. chrome input http://127.0.0.1:3000
  3. ctrl+c
  4. it wait the response return and terminate process after timeout