Allow your worker processes to die honourably
Seppuku (named after the highly ritual suicide ceremony of Japanese samurai) is a simple module that streamlines the process of gracefully shutting down worker processes in a Node.js cluster that serves web pages through restify or express.
It can be triggered manually, in response to an abnormal condition (e.g.: an unhandled exception), or automatically after a configurable number of requests to keep memory creep at bay.
By default, it stops the server from accepting connections, instructs the parent process to respawn, and gives existing connections a period of time to shut down, after which it automatically terminates the worker process.
To help prevent accidental hosing of your servers, these shutdown periods can be randomized so that two workers started at roughly the same time are less likely to enter seppuku at the same time.
npm install seppuku
var restify = require'restify';var seppuku = require'seppuku';var server = restifycreateServer;serveruseseppukuserver options;// Go about your business. You're done!
Seppuku's functionality can be altered by passing a number of options, listed below together with their respective defaults:
minDeferralTime: 5000 // Minimum time to wait between seppuku and kaishakumaxDeferralTime: 10000 // Maximum time to wait between seppuku and kaishakumaxRequests: 0 // Number of requests after which seppuku starts automatically (0 = never)trapExceptions: true // Whether exceptions should start seppukufilter: null // A filter function that determines the relative weight of a requestkaishakunin: null // An optional function that replaces the default termination handlerexitCode: 1 // The code to pass to process.exit();
When a seppuku operation starts, the default
kaishakunin (“beheader”) method shuts down the server and instructs the worker to disconnect from the parent process (which should then automatically spawn a new worker). Before terminating the worker process,
kaishakunin() waits a variable amount of time to allow existing requests to be terminated gracefully, after which, if any requests are still open, it calls
The termination time is randomized in order to minimize the impact that multiple workers started on or around the same time could have if they entered a seppuku at the same time. You can provide a set of
maxDeferralTime options to determine the randomization boundaries.
Seppuku has the ability to restart automatically after a certain number of requests have been processed by the server. This can be helpful if you have a creeping memory leak that you cannot attend to right away. You can set the
maxRequest option to any arbitrary number, or to zero if you want to turn off this functionality altogether.
By default, seppuku increments the request counter by one every time a request is processed, regardless of its type. If you wish, you can specify a
filter function that receives the current request alongside the current request count and can pass a different weight value depending on the type of request. For example, if you don't want
OPTIONS requests to be counted towards the request count, you could write this filter:
var options =maxRequests: 10000switchreqmethodtoLowerCasecase 'head':case 'options':return 0;return 1;seppukuserver options;
The default termination handler performs the following:
- Determine and set a timer for the termination time.
- Emit the
seppukuevent on the server instance
- Close the server
- Disconnect the worker (this step is safely skipped if the process is not running in a cluster)
- Terminate the process if the timer expires
Note that the timer itself is not retained; if all outstanding events (including existing requests) are resolved before the termination time, the process will terminate before the timer has expired.:
If the default termination handler doesn't do everything you need, you have a couple of options. The first is to trap the
seppuku event on the server object, which is emitted as soon as the seppuku process begins. It receives two parameters, the first the amount of time until the process will be terminated, and the second a cancellation callback (more about this later).
Here's an example:
serveron'seppuku'// Terminate outstanding long-running connectionsterminateAllConnections; // ...// `err` contains the exception that caused seppuku to start, if any;
If you desire a completely custom termination process, you can pass your own
kaishakunin method as an option—at which point you're completely on your own. For example:
var options =// Terminate immediatelyprocessexit255;// `err` contains the exception that caused seppuku to start, if any
server.seppuku(), and all will be handled for you.
You can terminate a seppuku operation by trapping the
seppuku event emitted by your server instance and calling the callback that the handler receives as its second parameter. This causes seppuku to be completely reset as if you were restarting the server.
Contributions are welcome, particularly if accompanied by a unit test.
Copyright © 2013 Marco Tabini & Associates, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.