node.js performance optimization and single threaded architecture - node.js

I'm running a Node.js app with express and want to start increasing its performance. Several routes are defined. Let's have an basic example:
app.get('/users', function (req, res) {
User.find({}).exec(function(err, users) {
res.json(users);
}
});
Let's assume we have 3 clients A, B and C, who try to use this route. Their requests arrive on the server in the order A, B, C with 1 millisecond difference in between.
1. If I understand the node.js architecture correctly, every request will be immediately handled, because Users.find() is asynchronous and there is non-blocking code?
Let's expand this example with a synchronous call:
app.get('/users', function (req, res) {
var parameters = getUserParameters();
User.find({parameters}).exec(function(err, users) {
res.json(users);
}
});
Same requests, same order. getUserParameters() takes 50 milliseconds to complete.
2. A will enter the route callback-function and blocks the node.js thread for 50 milliseconds. B and C won't be able to enter the function and have to wait. When A finishes getUsersParameters() it will continue with the asynchronous User.find() function and B will now enter the route callback-function. C will still have to wait for 50 more milliseconds. When B enters the asynchronous function, C's requests can be finally handled. Taken together: C has to wait 50 milliseconds for A to finish, 50 milliseconds for B to finish and 50 milliseconds for itself to finish (for simplicity, we ignore the waiting time for the asynchronous function)?
Assuming now, that we have one more route, which is only accessible by an admin and will be called every minute through crontab.
app.get('/users', function (req, res) {
User.find({}).exec(function(err, users) {
res.json(users);
}
});
app.get('/admin-route', function (req, res) {
blockingFunction(); // this function takes 2 seconds to complete
});
3. When a request X hits admin-route and blockingFunction() is called, will A,B and C, who will call /users right after X's request have to wait 2 seconds until they even enter the route callback-function?
4. Should we make every self defined function, even if it takes only 4 milliseconds, as an asynchronous function with a callback?

The answer is "Yes", on #3: blocking means blocking the event loop, meaning that any I/O (like handling an HTTP request) will be blocked. In this case, the app will seem unresponsive for those 2 seconds.
However, you have to do pretty wild things for synchronous code to take 2 seconds (either very heavy calculations, or using a lot of the *Sync() methods provided by modules like fs). If you really can't make that code asynchronous, you should consider running it in a separate process.
Regarding #4: if you can easily make it asynchronous, you probably should. However, just having your synchronous function accept a callback doesn't miraculously make it asynchronous. It depends on what the function does if, and how, you can make it async.

The ground principle is anything locking up the CPU (long-running for loops for instance) or anything using I/O or the network must be asynchronous. You could also consider moving out CPU-intensive logic out of node JS, perhaps into a Java/Python module which exposes a WebService which node JS can call.
As an aside, take a look at this module (might not be production-ready). It introduces the concept of multithreading in NodeJS: https://www.npmjs.com/package/webworker-threads

#3 Yes
#4 Node.js is for async programming and hence its good to follow this approach to avoid surprises in performance
Meanwhile, you can use cluster module of Node.js to improve performance and throughput of your app.

You may need to scale your application vertically first. Check out Node.js cluster module. You may utilize all the cores of the machine by spawning up workers on each core. A cluster is a pool of similar workers running under a parent Node process. Workers are spawned using the fork() method of the child_processes module. This means workers can share server handles and use inter-process communication to communicate with the parent Node process.
var cluster = require('cluster')
var http = require('http')
var os = require('os')
var numCPUs = os.cpus().length
if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
cluster.fork()
}
} else {
// Define express routes and listen here
}

Related

In Nodejs context, why the phrase "Don't Block Event Loop" exists even in development it can't be avoided?

Imagine a simple express web app like below.
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
takeTimePlain()
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
function takeTimePlain () {
/* Long Job Operation Simulation */
let date = Date.now();
let end = Date.now() + 5000;
while (date < end ) {
console.log ("Iterating through while loop")
date = Date.now()
}
return
}
Then I'm calling http://localhost:3000 3 times concurrently. If I inspect the latency or some call it server waiting time,
If latency for the first request is x
then for the second request it takes 2x time
and for the third request it takes 3x time to get completed
If we assume 10 client's connect to endpoint at the same time, then the 10th client should wait 10x time to get a response. Imaging a few thousand requests per second.
Why Nodejs being praise for it's scalability, event driven and single threaded nature even it can't handle common scenario like above without help of out side solutions like running multiple instances of the same service?
Is there any non-blocking alternative to takeTimePlain() ?
I'm aware about setTimeout(cb, 5000), However my intention here isn't about waiting 5 seconds. This takeTimePlain() can be anything like sorting through and array, ordering, find, etc. Its a common coding solution to replicate a process which takes cpu time.
Edit: This question isn't about skepticism or suggestions. This is about clarifying facts after showing actual benchmarks. Feel free to edit the title if it doesn't fit the content.
There are a few parts to this answer.
First node is not multithreaded, by nature it does all its work asynchronous. The reason callbacks and promises are everywhere. You takeTimePlain is intentionally not asynchronous so is not very common. Your example is inherently going against the paradigm.
But if it does happen there are a few options:
A) spawn another process to run long blocking code.
B) use cluster.
Cluster will allow you to open as many node https processes as you like on the same port. https://nodejs.org/api/cluster.html There are a lot of solutions like cluster but cluster is the build it one.
For fixing takeTimePlain you could instead of a loop use setInterval with a time of 0. Then clear it once you hit the end. Though that would be very slow.
One last thing node/express is no more scalable than any other solution.

node.js: How to lock/synchronize a block of code?

Let's take the simple code snippet:
var express = require('express');
var app = express();
var counter = 0;
app.get('/', function (req, res) {
// LOCK
counter++;
// UNLOCK
res.send('hello world')
})
Let's say that app.get(...) is called a huge number of times, and as you can understand I don't want the line counter++ to be executed concurrently by the two different threads.
Therefore, I want to lock this line that only one thread can have access to this line. My question is how to do it in node.js?
I know there is a lock package: https://www.npmjs.com/package/locks, but I'm wondering whether there is a "native" way of doing it without an external library.
I don't want the line counter++ to be executed concurrently by the two different threads
That cannot happen in node.js with just regular Javascript coding.
node.js is single threaded and event-driven, so there's only ever one piece of Javascript code running at a time that can access that variable. You do not have to worry about the typical pre-emptive concurrency issues of multi-threaded systems.
That said, you can still have concurrency issues in node.js if you are using asynchronous code because the node.js asynchronous model returns control back to the system to process the next event and the asynchronous callback gets called on some future event. But, the concurrency issues are non-pre-emptive so you fully control when they can occur.
If you show us your actual code in your app.get() route handler, then we can advise more specifically about whether you do or don't have a concurrency issue there or not. And, if you do, we can advise on how to best deal with that.
Threads in the thread pool are all native code that runs behind the scenes. They only trigger actual Javascript to run by queuing events through the event queue. So, because all Javascript that runs is serialized through the event queue, you only get one piece of Javascript ever running at a time. The basic scheme of the event queue is that the interpreter runs a piece of Javascript until it returns control back to the system. At that point, the interpreter looks in the event queue and if there's an event waiting, it pulls that event out and calls the callback associated with that event. Meanwhile, if there is native code running in the background, when it completes, it adds an event to the event queue. That event is not processed until the current Javascript returns control back to the system and it can then grab the next event out of the event queue. So, it's this event-queue that serializes running only one piece of Javascript at a time.
Edit: Nodejs does now have WorkerThreads which enable separate threads of Javascript, but each thread has its own heap and its own variables so a variable from one thread cannot be directly accessed from another thread. You can configure shared memory that both WorkerThreads can access, but that isn't straight variables, but blocks of memory and if you want to use shared memory, then you do indeed need to code your own synchronization methods to make sure you are atomically accessing the variable. The code you show in your question is not using any of this so the access to the counter variable is already atomic and cannot be simultaneously accessed by any other Javascript, even if you are using WorkerThreads.
If you block thread none of the requests will execute all will be in the queue.
It 's not good practice to block the thread in Node.js
var express = require('express');
var app = express();
var counter = 0;
const getPromise = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Done')
}, 100);
});
}
app.get('/', async (req, res) => {
const localCounter = counter++;
// Use local counter for rest of operation so value won't vary
// LOCK: Use promise/callback
await getPromise(); // Not locked but waiting for getPromise to finish
console.log(localCounter); // Same value before lock
res.send('hello world')
})
Node.js is single-threaded, which means that any single process running your app will not have data races like you anticipate. In fact, a quick inspection of the locks library shows that they use a boolean flag and a system of Array objects to determine whether something is locked or not.
You should only really worry about this if you plan on sharing data with multiple processes. In that case, you could use Alan's lockfile approach from this stackoverflow thread here.

Async callback blocks NodeJS

I have a server-client based NODE.JS application.
server.js
...
socket.on('message', function(message) {
if(message.code == 103)
{
process_some_data()
}
else
{
console.log("UNKNOWN MESSAGE");
}
});
...
client.js
.. sending responses back to server.js
the process_some_data() function takes about 4 seconds to complete, and when i have just one client it is not a problem, but if i have 10, they all choke and wait till the the last finishes.
I found out that the entire socket event waits till he finishes the current job, for example if i comment process_some_data(), it will not be frozen
I have tried 2 tweaks but the didn't worked :
...
socket.on('message', function(message) {
if(message.code == 103)
{
setTimeout(function() {
process_some_data();
console.log("FINISH");
}, 1)
}
else
{
console.log("UNKNOWN MESSAGE");
}
});
...
And even used http://caolan.github.io/async/ ,but no use :
...
socket.on('message', function(message) {
if(message.code == 103)
{
// Array to hold async tasks
var asyncTasks = [];
async.series([
setTimeout(function() {
process_some_data();
console.log("FINISH");
}, 1)
], function (err, results) {
console.log(results);
});
}
else
{
console.log("UNKNOWN MESSAGE");
}
});
...
How to make this ASYNC? Really need this.
Thank you.
You need multiple processes to solve this with Javascript, because Javascript engines are single-threaded.
What?
When it comes to handling I/O events, such as reading a socket, writing to a file or waiting for a signal, Javascript engines give the appearance of doing multiple things at the same time.
They are actually not: it's just that, under most conditions, processing these events takes so little computation, and the events themselves occur with so much time in between (a microsecond is an eternity for a CPU), that the engine can just process them one after another with plenty of time to spare.
In human time-scale, it looks like the engine is doing a lot of stuff in parallel, but it's just working serially at great speed.
No matter how you schedule your code to run, using setTimeout or Promise, it will still block other events from being processed during the time it's actively computing. Long-running computations (in the scale of seconds, instead of milliseconds) expose the single-threaded nature of the engine: it cannot actually do multiple things at the same time.
Multiple processes
Your computer, however, probably has multiple CPU cores. Unlike the Javascript engine, your hardware is capable of tackling multiple tasks at the same time, at least 1 per core. Even with a single core, your operating system can solve the problem if you run multiple processes.
Since a single Javascript process is single-threaded, you need multiple Javascript processes for this. An easy and time-proven architecture to solve your problem is this:
One Javascript program, running in one process, reads from the socket. Instead of calling process_some_data(), however, it puts all incoming messages in a queue.
This program then sends items from the queue to another Javascript program, running in a different process, that performs the computation using another CPU core. There may be multiple copies of this second process. In a modern computer, it makes sense to have twice as many active processes as you have CPU cores.
A simple approach for Node is to write an HTTP server, using express, that runs the computationally-intensive task. The main program can then use HTTP to delegate tasks to the workers, while still being able to read from the socket.
This is a good article on the topic of multi-processing with Node, using the cluster API.

Parallel Request at different paths in NodeJS: long running path 1 is blocking other paths

I am trying out simple NodeJS app so that I could to understand the async nature.
But my problem is as soon as I hit "/home" from browser it waits for response and simultaneously when "/" is hit, it waits for the "/home" 's response first and then responds to "/" request.
My concern is that if one of the request needs heavy processing, in parallel we can't request another one? Is this correct?
app.get("/", function(request, response) {
console.log("/ invoked");
response.writeHead(200, {'Content-Type' : 'text/plain'});
response.write('Logged in! Welcome!');
response.end();
});
app.get("/home", function(request, response) {
console.log("/home invoked");
var obj = {
"fname" : "Dead",
"lname" : "Pool"
}
for (var i = 0; i < 999999999; i++) {
for (var i = 0; i < 2; i++) {
// BS
};
};
response.writeHead(200, {'Content-Type' : 'application/json'});
response.write(JSON.stringify(obj));
response.end();
});
Good question,
Now, although Node.js has it's asynchronous nature, this piece of code:
for (var i = 0; i < 999999999; i++) {
for (var i = 0; i < 2; i++) {
// BS
};
};
Is not asynchronous actually blocking the node main thread. And therefore, all other requests has to wait until this big for loop will end.
In order to do some heavy calculations in parallel I recommend using setTimeout or setInterval to achieve your goal:
var i=0;
var interval = setInterval(function() {
if(i++>=999999999){
clearInterval(interval);
}
//do stuff here
},5);
For more information I recommend searching for "Node.js event loop"
As Stasel, stated, code running like will block the event loop. Basically whenever javascript is running on the server, nothing else is running. Asynchronous I/O events such as disk I/O might be processing in the background, but their handler/callback won't be call unless your synchronous code has finished running. Basically as soon as it's finished, node will check for pending events to be handled and call their handlers respectively.
You actually have couple of choices to fix this problem.
Break the work in pieces and let the pending events be executed in between. This is almost same as Stasel's recommendation, except 5ms between a single iteration is huge. For something like 999999999 items, that takes forever. Firstly I suggest batch process the loop for about sometime, then schedule next batch process with setimmediate. setimmediate basically will schedule it after the pending I/O events are handled, so if there is not new I/O event to be handled(like no new http requests) then it will executed immediately. It's fast enough. Now the question comes that how much processing should we do for each batch/iteration. I suggest first measure how much does it on average manually, and for schedule about 50ms of work. For example if you have realized 1000 items take 100ms. Then let it process 500 items, so it will be 50ms. You can break it down further, but the more broken down, the more time it takes in total. So be careful. Also since you are processing huge amount of items, try not to make too much garbage, so the garbage collector won't block it much. In this not-so-similar question, I've explained how to insert 10000 documents into MongoDB without blocking the event loop.
Use threads. There are actually a couple nice thread implementations that you won't shoot yourself in foot with them. This is really a good idea for this case, if you are looking for performance for huge processings, since it would be tricky as I said above to implement CPU bound task playing nice with other stuff happening in the same process, asynchronous events are perfect for data-bound task not CPU bound tasks. There's nodejs-threads-a-gogo module you can use. You can also use node-webworker-threads which is built on threads-a-gogo, but with webworker API. There's also nPool, which is a bit more nice looking but less popular. They all support thread pools and should be straight forward to implement a work queue.
Make several processes instead of threads. This might be slower than threads, but for huge stuff still way better than iterating in the main process. There's are different ways. Using processes will bring you a design that you can extend it to using multiple machines instead of just using multiple CPUs. You can either use a job-queue(basically pull the next from the queue whenever finished a task to process), a multi process map-reduce or AWS elastic map reduce, or using nodejs cluster module. Using cluster module you can listen to unix domain socket on each worker and for each job just make a request to that socket. Whenever the worker finished processing the job, it will just write back to that particular request. You can search about this stuff, there are many implementations and modules existing already. You can use 0MQ, rabbitMQ, node built-in ipc, unix domain sockets or a redis queue for multi process communications.

NodeJs how to create a non-blocking computation

I am trying to get my head around creating a non-blocking piece of heavy computation in nodejs. Take this example (stripped out of other stuff):
http.createServer(function(req, res) {
console.log(req.url);
sleep(10000);
res.end('Hello World');
}).listen(8080, function() { console.log("ready"); });
As you can imagine, if I open 2 browser windows at the same time, the first will wait 10 seconds and the other will wait 20, as expected. So, armed with the knowledge that a callback is somehow asynchronous I removed the sleep and put this instead:
doHeavyStuff(function() {
res.end('Hello World');
});
with the function simply defined:
function doHeavyStuff(callback) {
sleep(10000);
callback();
}
that of course does not work... I have also tried to define an EventEmitter and register to it, but the main function of the Emitter has the sleep inside before emitting 'done', for example, so again everything will run block.
I am wondering here how other people wrote non-blocking code... for example the mongojs module, or the child_process.exec are non blocking, which means that somewhere down in the code either they fork a process on another thread and listen to its events. How can I replicate this in a metod that for example has a long process going?
Am I completely misunderstanding the nodejs paradigm? :/
Thanks!
Update: solution (sort of)
Thanks for the answer to Linus, indeed the only way is to spawn a child process, like for example another node script:
http.createServer(function(req, res) {
console.log(req.url);
var child = exec('node calculate.js', function (err, strout, strerr) {
console.log("fatto");
res.end(strout);
});
}).listen(8080, function() { console.log("ready"); });
The calculate.js can take its time to do what it needs and return. In this way, multiple requests will be run in parallel so to speak.
You can't do that directly, without using some of the IO modules in node (such as fs or net). If you need to do a long-running computation, I suggest you do that in a child process (e.g. child_process.fork) or with a queue.
We (Microsoft) just released napajs that can work with Node.js to enable multithreading JavaScript scenarios in the same process.
your code will then look like:
var napa = require('napajs');
// One-time setup.
// You can change number of workers per your requirement.
var zone = napa.zone.create('request-worker-pool', { workers: 4 });
http.createServer(function(req, res) {
console.log(req.url);
zone.execute((request) => {
var result = null;
// Do heavy computation to get result from request
// ...
return result;
}, [req]).then((result) => {
res.end(result.value);
}
}).listen(8080, function() { console.log("ready"); });
You can read this post for more details.
This is a classic misunderstanding of how the event loop is working.
This isn't something that is unique to node - if you have a long running computation in a browser, it will also block. The way to do this is to break the computation up into small chunks that yield execution to the event loop, allowing the JS environment to interleave with other competing calls, but there is only ever one thing happening at one time.
The setImmediate demo may be instructive, which you can find here.
If you computation can be split into chunks, you could schedule executor to poll for data every N seconds then after M seconds run again. Or spawn dedicated child for that task alone, so that the main thread wouldn't block.
Although this is an old post(8 years ago), try to add some new updates to it.
For Nodejs application to get good performance, the first priority is never blocking the event loop. The sleep(10000) method breaks this rule. This is also the reason why Node.js is not suitable for the CPU intensive application. Since the big CPU computation occurs on the event loop thread(it's also the main and single thread of node.js)and will block it.
Multithread programming work_threads was introduced into node.js ecosystem since version 12. Compared with multi-process programming, it's lightweight and has less overhead.
Although multithread was introduced into node.js, but Node.js is still based on the event driven model and async non-block IO. That's node.js's DNA.

Resources