Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: App Router docs for gracefully handling shutdowns still requires pages/_document.js #51404

Open
osdiab opened this issue Jun 16, 2023 · 12 comments
Labels
Documentation Related to Next.js' official documentation. linear: dx Confirmed issue that is tracked by the DX team.

Comments

@osdiab
Copy link

osdiab commented Jun 16, 2023

What is the improvement or update you wish to see?

The docs for NextJS using the App Router for Manual graceful shutdowns use the pages/_document.js file to handle shutdown signals, though my project doesn't even have a pages/ directory anymore now that everything is moved over to App Router.

The migration docs from the Pages to App Router even has a section on getting rid of the _document.js file, so I would assume this is not supposed to be the way moving forward. Am I wrong?

Is it still the "proper" way to handle this, or is there some other App Router-native method of handling shutdown signals? Thanks!

Is there any context that might help us understand?

I posted a discussion about this but figured that this might be a useful docs improvement (or maybe a missing piece of functionality with the App Router).

Does the docs page already exist? Please link to it.

https://nextjs.org/docs/app/building-your-application/deploying#manual-graceful-shutdowns

DX-1692

@osdiab osdiab added the Documentation Related to Next.js' official documentation. label Jun 16, 2023
@balazsorban44
Copy link
Member

Hi, when self-hosting, the best place to do this might be the emitted server.js file instead, exactly what's the current default already:

// Allow the graceful termination to be manually configurable
if (!process.env.NEXT_MANUAL_SIG_HANDLE) {
process.on('SIGTERM', () => process.exit(0))
process.on('SIGINT', () => process.exit(0))
}

If you need to override it, you can do so by adding the NEXT_MANUAL_SIG_HANDLE environment variable and adding your own handler.

The documentation on this can be updated to reference server.js instead of pages/_document

@balazsorban44 balazsorban44 added the linear: dx Confirmed issue that is tracked by the DX team. label Jun 26, 2023
@osdiab
Copy link
Author

osdiab commented Jun 27, 2023

Thanks for the reply - i'm not sure I follow, are you saying that one needs to write a build script to manually patch the generated server.js file? or that you can put a file called server.js in your project to override the default?

If that's the default behavior, it seems that deploying an app on Render's free tier doesn't cause it to be triggered gracefully, I just kept on getting errors reported whenever the app went to sleep.

@ncrmro
Copy link

ncrmro commented Jul 23, 2023

I'm also wondering about this, was hoping to close a database connection on sigterm.

https://github.com/ncrmro/nextjs-sqlite

@uyeong
Copy link

uyeong commented Sep 7, 2023

In App routes, the code below doesn't seem to work, even though I specify NEXT_MANUAL_SIG_HANDLE as true.

It closes before the graceful shutdown code writed in server.ts (or js) is executed.

@mdio
Copy link
Contributor

mdio commented Oct 4, 2023

@balazsorban44 could you maybe give an example how your proposed solution would work?
For standalone the build process will output a server.js which contains the default signal handlers.
To register our own, we can set the environment variable NEXT_MANUAL_SIG_HANDLE but then where should the custom signal handling code be put?
I can confirm that _document.js from the graceful shutdowns documentation is not working.

Without this we currently experience a downtime of a few seconds every time a Kubernetes pod with Next.js is shut down because it still receives new requests during the shutdown.

@jschmidtmac
Copy link

jschmidtmac commented Oct 4, 2023

@balazsorban44 could you maybe give an example how your proposed solution would work? For standalone the build process will output a server.js which contains the default signal handlers. To register our own, we can set the environment variable NEXT_MANUAL_SIG_HANDLE but then where should the custom signal handling code be put? I can confirm that _document.js from the graceful shutdowns documentation is not working.

Without this we currently experience a downtime of a few seconds every time a Kubernetes pod with Next.js is shut down because it still receives new requests during the shutdown.

Including NEXT_MANUAL_SIG_HANDLE=true in my .env and setting the handler in the layout.tsx file like so solves the problem for me. Needs to be called in the layout function.

@mdio
Copy link
Contributor

mdio commented Oct 5, 2023

Thank you for help!
We're using the Pages Router so layout.tsx is not available. But I tried _document.tsx and it doesn't matter if I put the signal handling code (below) inside the exported render function or outside of it.
The result is always that "manual signal handling active" is logged (when passing NEXT_MANUAL_SIG_HANDLE via env) but e.g. "Received SIGINT..." is not logged.

If I manually edit the standalone server.js and add the same listener functions to the event handling there, the "Received SIGINT..." is logged.
However, I don't think it's a very stable solution to monkey-patch a generated file.
It would be great to have a proper way to hook into signals like it is documented.

The signal handling code I use:

if (process.env.NEXT_MANUAL_SIG_HANDLE) {
  console.log('manual signal handling active');
  process.on('SIGTERM', () => {
    console.log('Received SIGTERM: ', 'cleaning up');
    process.exit(0);
  });

  process.on('SIGINT', () => {
    console.log('Received SIGINT: ', 'cleaning up');
    process.exit(0);
  });
}

@higgins
Copy link

higgins commented Oct 13, 2023

As many have observed in this issue, the feature appears to not work. Since this ticket was logged about changing the reference from pages/_document.js, I've reported the bug independently here:

#56810

@higgins
Copy link

higgins commented Oct 13, 2023

@balazsorban44 could you maybe give an example how your proposed solution would work? For standalone the build process will output a server.js which contains the default signal handlers. To register our own, we can set the environment variable NEXT_MANUAL_SIG_HANDLE but then where should the custom signal handling code be put? I can confirm that _document.js from the graceful shutdowns documentation is not working.
Without this we currently experience a downtime of a few seconds every time a Kubernetes pod with Next.js is shut down because it still receives new requests during the shutdown.

Including NEXT_MANUAL_SIG_HANDLE=true in my .env and setting the handler in the layout.tsx file like so solves the problem for me. Needs to be called in the layout function.

@jschmidtmac By registering the process.on event handler within the layout function, I suspect this will introduce a memory leak. Each request will register the handler, no?

@jschmidtmac
Copy link

@balazsorban44 could you maybe give an example how your proposed solution would work? For standalone the build process will output a server.js which contains the default signal handlers. To register our own, we can set the environment variable NEXT_MANUAL_SIG_HANDLE but then where should the custom signal handling code be put? I can confirm that _document.js from the graceful shutdowns documentation is not working.
Without this we currently experience a downtime of a few seconds every time a Kubernetes pod with Next.js is shut down because it still receives new requests during the shutdown.

Including NEXT_MANUAL_SIG_HANDLE=true in my .env and setting the handler in the layout.tsx file like so solves the problem for me. Needs to be called in the layout function.

@jschmidtmac By registering the process.on event handler within the layout function, I suspect this will introduce a memory leak. Each request will register the handler, no?

Good point. We thought that and just tried it anyways, when testing we didn't notice any leaks, we have monitored this since the update as well and haven't noticed anything. This could be with the size of our application/user base. Would think on a larger scale it would cause that. With layout.js replacing both _app.js and _document.js that is kinda where our minds went.

@lukevers
Copy link

So... NEXT_MANUAL_SIG_HANDLE does not seem to be working for me. I just spent a few minutes on an idea, but am trying to figure out how crazy I am:

const process = require('process');
const spawn = require('child_process').spawn;

// Adding `detatched: true` makes sure the signal doesn't send down automatically
const server = spawn('pnpm', ['run', 'start'], { detached: true });
server.stdout.on('data', function(data) {
  process.stdout.write(data);
});

server.stderr.on('data', function (data) {
  process.stderr.write(data);
});

server.on('exit', function (code) {
  console.log('Application exited with code:', code.toString());
});

['SIGTERM', 'SIGINT'].forEach((signal) => {
  process.on(signal, function (signal) {
    console.log('Caught signal', signal, '-- Allowing requests to drain');

    setTimeout(() => {
      console.log('Terminating Application')
      server.kill(signal);
    }, 15 * 1000);

    setTimeout(function() {
      console.log('Terminating Wrapper');
      process.exit(0);
    }, 20 * 1000);
  });
});

@yubinTW
Copy link

yubinTW commented Mar 19, 2024

I added process.on function in instrumentation.ts

// instrumentation.ts
export function register() {

  if (process.env.NEXT_RUNTIME === 'nodejs') {
       if (process.env.NEXT_MANUAL_SIG_HANDLE) {
        process.on('SIGTERM', () => {
          /** graceful shutdown **/
        })
      }
  }

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Documentation Related to Next.js' official documentation. linear: dx Confirmed issue that is tracked by the DX team.
Projects
None yet
Development

No branches or pull requests

9 participants