Open osdiab opened 1 year ago
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:
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
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.
I'm also wondering about this, was hoping to close a database connection on sigterm.
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.
@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.
@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 variableNEXT_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.
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);
});
}
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:
@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 variableNEXT_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?
@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 variableNEXT_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.
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);
});
});
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 **/
})
}
}
}
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 **/ }) } } }
Can confirm this works - thanks @yubinTW ! This is probably the best workaround so far until the team adds in a shutdown hook. Remember to put instrumentation.ts
in your src
folder and enable the instrumentationHook on the config
What is the improvement or update you wish to see?
The docs for NextJS using the App Router for
Manual graceful shutdowns
use thepages/_document.js
file to handle shutdown signals, though my project doesn't even have apages/
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