koajs / koa

Expressive middleware for node.js using ES2017 async functions
https://koajs.com
MIT License
35.21k stars 3.23k forks source link

Can't kill a specific worker in Node.js cluster using Koa framework #1235

Closed neeraj-kh closed 6 years ago

neeraj-kh commented 6 years ago

We are using a c++ dll from Node.js server using FFI node module. The C++ dll exposes many functions, that the Node.js server is using.

The C++ dll has a characteristic of getting stuck, in some special scenario. And as yet, we do not have a mechanism to heal this up from within the C++ dll. Due to this, the forthcoming requests go un-addressed, because the dll is single-threaded and can take only one request at a time.

The reason we had to use the cluster-worker topology because, we wanted to control the dll from outside.

We have created a fixed number of forks/workers and they are in a pool. For every incoming request, we are assigning the request to a new worker and using the worker with the dll. Whenever the worker is seen taking more than a "healthy" amount of time, we wish to kill it.

We are facing a specific problem, where we are unable to kill a worker, in case the worker is stuck with the c++ dll not responding. This is needed, because of un-handled cases in the c++ dll.

We referred the code for cluster module of Node.js -https://nodejs.org/api/cluster.html#cluster_worker_process

Here are our code snippets -

server_fork.js

var chalk = require( "chalk" ); var cluster = require( "cluster" ); var os = require( "os" );

if ( cluster.isMaster ) { console.log( chalk.red( "[Cluster]" ), "Master process is now running.", process.pid );

for ( var i = 0, coreCount = 2; i < coreCount ; i++ ) {
    var worker = cluster.fork();
}

function messageHandler(msg) {
    console.log('messageHandler')
    if (msg.cmd && msg.cmd === 'notifyRequest') {
      numReqs += 1;
    }
  }

cluster.on(
    "exit",
    function handleExit( worker, code, signal ) {

        console.log( chalk.yellow( "[Cluster]" ), "Worker has died.", worker.process.pid );
        console.log( chalk.yellow( "[Cluster]" ), "Death was suicide:", worker.exitedAfterDisconnect );

            var worker = cluster.fork();
            console.log('new thread');
    }
);

cluster.on(
    "message",
    function handleExit( worker, code, signal ) {
        let timeout
        if (code.cmd == 'started'){
            worker.toBeKilled = true
            timeout = setTimeout(() => {
                if(worker.toBeKilled){
                    try{
                    console.log('inside worker to be killed')
                    // worker.disconnect()
                    worker.kill()
                    //process.exit(0)
                    // process.exit()
                    }catch(err){
                        console.log('err',err)
                    }

                }
              }, 2000);
        } else {
            worker.toBeKilled = false
        }

    }
);
function messageHandler(msg) {
    if (msg.cmd && msg.cmd === 'notifyRequest') {
        console.log('notifyRequest');
    }
  }

} else {

require( "./server" );
console.log( chalk.red( "[Worker]" ), "Worker has started.", process.pid );

} server.js

const Koa = require('koa'); var mount = require('koa-mount'); var serve = require('koa-static'); var bodyParser = require('koa-bodyparser'); var koaBody = require('koa-body'); var api = require('./../src/modules/bvp/router.js'); var jwt = require('koa-jwt');

const app = new Koa(); var hbs = require('koa-hbs'); const convert = require('koa-convert'); const co = require('co');

app.use(convert(hbs.middleware({ viewPath: __dirname + '/../src/views', })));

app.use(async (ctx, next) => { const render = ctx.render; ctx.render = async function _convertedRender () { return co.call(ctx, render.apply(ctx, arguments)); } await next(); });

if (!process.env.NODE_ENV || process.env.NODE_ENV === 'devlopment') { app.use(mount('/swagger', serve(${process.cwd()}/../swagger))) }

app.use(koaBody()); app.use(bodyParser()); app.use(api.routes());

app.listen(3000, function() { console.log('server started on port 3000'); });

module.exports = app; controller.js

var som_lib = require('som_lib.js'); const Promise = require('promise'); async function simulate(ctx) { var arr = []; process.send({ cmd: 'started' }); var output = som_lib.playLeague(); process.send({ cmd: 'end' }); ctx.body = output; } module.exports = { simulate: simulate } In the case of a timeout, when we are trying to kill that particular worker, we are getting the following error -

err { Error: kill ESRCH at exports._errnoException (util.js:1050:11) at process.kill (internal/process.js:188:13) at Timeout.setTimeout [as _onTimeout] (C:\Data\projects\BVP_backend - Copy\bin\server_fork.js:65:33) at ontimeout (timers.js:386:14) at tryOnTimeout (timers.js:250:5) at Timer.listOnTimeout (timers.js:214:5) code: 'ESRCH', errno: 'ESRCH', syscall: 'kill' } We have already referred a similar problem in Express framework - link https://stackoverflow.com/questions/42106004/cant-kill-a-specific-worker-in-node-js-cluster

fl0w commented 6 years ago

Unless I've misunderstood ESRCH error message (which indicates job/process not being found), this isn't related to Koa. I'd like to refer you to any other forum such as Stack Overflow, IRC, Gitter or Reddit.

If you feel I've misunderstood the problem, please remove all unrelated code and re-open this ticket. Sorry if this feels harsh but this issue tracker is mainly used to contribute and discuss Koa related issues.