Home > Blockchain >  Expressjs main loop blocked during intense operation
Expressjs main loop blocked during intense operation

Time:07-18

I'm having an expressjs server running in which an endpoint init performs some intense operation that has an average completion time of 10 seconds. During these 10 seconds, the main loop is "stuck", making it impossible to send requests to the expressjs server. I've been googling for a while now but found nothing which would enable expressjs to handle requests concurrently. It would seem silly if this is not possible. For any hints or help, I'm very thankful.

Example code:

routes.js

app.route('/v1/cv/random').get(init);

features/init.js

module.exports = async function init(req, res) {
    try {
        // perform some time consuming operation here
        res.status(201).send(someVar);
    } catch (err) {
        res.status(500).send(`failed to init`).end();
    }
};

CodePudding user response:

It is possible to implement algorithms with long running time in a synchronous manner, for example the Tower of Hanoi:

function move(from, to, via, n) {
  if (n > 1)
    move(from, via, to, n - 1);
  to.unshift(from.shift());
  if (n > 1)
    move(via, to, from, n - 1);
}
app.get("/tower", function(req, res) {
  var a = [];
  for (var i = 0; i < Number(req.query.n); i  ) a.push(i);
  var b = [];
  var c = [];
  move(a, b, c, a.length);
  res.end("Done");
});

Invoking GET /tower?n=<N> with large enough <N> will indeed block the main loop of express.

This blocking can be avoided by introducing asynchronousness into the algorithm, for example with setTimeout(nextAlgorithmicStep) commands. This puts the nextAlgorithmicStep function in a queue, but the same queue also has room for functions that process concurrent requests:

function tick(from, to, via, n) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      move(from, to, via, n, resolve);
    });
  });
}
async function move(from, to, via, n, resolve) {
  if (n > 1)
    await tick(from, via, to, n - 1);
  to.unshift(from.shift());
  if (n > 1)
    await tick(via, to, from, n - 1);
  resolve();
}
app.get("/tower", async function(req, res) {
  var a = [];
  for (var i = 0; i < Number(req.query.n); i  ) a.push(i);
  var b = [];
  var c = [];
  await tick(a, b, c, a.length);
  res.end("Done");
});

With this, you can you wait (forever) for the request GET /tower?n=64 to come back, but you can at least still make concurrent requests to the same server. (Using simply Promise or process.nextTick instead of setTimeout is not "asynchronous enough" to allow concurrent requests to be processed in between.)

However, the execution of GET /tower?n=10, which finished "immediately" in the first version, now takes much longer. It would be better to use the setTimeout not on all n levels of recursion, but only on every tenth level or so. You have to find similar good points for asynchronousness in your RSA algorithm.


That's what you can do with a single-threaded Node.js program. But there is an alternative that uses multiple Node.js threads.

CodePudding user response:

Did you tried Promise function in there? so that it wont affect main loop.

  • Related