We know Node.js for its lightning-fast performance. Yet, as with any language, you can write Node.js code that performs worse for your users than you’d like. To combat this, we need adequate performance testing. Today, we will cover just that with an in-depth look at how to set up and run a performance test and analyze the results so you can make lightning-fast Node.js applications.
The best way to understand how to performance test your application is to walk through an example. You can use Node.js for many purposes: writing scripts for running tasks, running a web server, or serving static files, such as a website. Today, we’ll walk through the steps to test a Node.js HTTP web API. But if you’re building something else in Node, don’t worry—a lot of the principles will be similar.
Tip: Find application errors and performance problems instantly with Stackify Retrace. Troubleshooting and optimizing your code is easy with integrated errors, logs and code level performance insights. Before we begin, let’s take a quick look at one of the more unique characteristics of Node.js performance. We’ll need to have knowledge of these characteristics later when we run our performance tests. What am I talking about? The big consideration for Node.js applications is their single-threaded, run-to-completion behavior—facilitated by what’s known as the event loop.
Now, I know what you’re thinking: that’s a lot. So let’s break this down a little so we understand what these mean. Let’s start with single threading. Threading, as a concept, allows concurrent processing within an application.
Node.js doesn’t have this capability, at least not in the traditional sense. Instead, to write applications that perform multiple tasks at once, we have the asynchronous code and the event loop.
What is the event loop? The event loop is Node.js’s way of breaking down long-running processes into small chunks.
It works like a heartbeat: Every few milliseconds, Node.js will check a work queue to start new tasks. If there is work, it’ll bring these onto the call stack and then run them to completion (we’ll talk about run-to-completion soon).
By breaking tasks down, Node.js can multi-task, which is your substitute for threading.
That means that while one task is waiting, another can start. So, rather than threading, we use async code, facilitated by programming styles like callbacks, promises, and async/await.
Most of the out-of-the-box Node APIs have both a synchronous and asynchronous method of execution.
Okay, so maybe you’re wondering: what does all this techno-jargon have to do with performance? Let me explain…. Imagine you’re building a Node.js application with two endpoints: one for file uploading, and one that fetches a user profile.
The user profile API will probably be requested significantly more often than the file upload, and if it doesn’t respond quick enough, it’ll block every page load for every user—not good.
The user upload API is used infrequently. Also, users expect uploading of tasks to take time, but they’re a lot less forgiving with page load times.
If we do not program with the event loop in mind, while the file is uploading, Node.js could end up hogging all the system resources and could block other users from using your application—uh-oh!
And that’s why you need to understand Node.js’s single-threaded nature. As we change our application, we need to consider this behavior.
We want to avoid doing long-running tasks synchronously, such as making network requests, writing files, or performing a heavy computation. Now we know about Node.js’s single-threaded nature, we can use it to our advantage. Let’s go step by step how you can set up, run, and analyze a performance test of your Node.js application to make sure you’re doing your best to leverage Node.js performance capabilities.
First, you’ll want to choose a tool that will allow you to run your performance tests. There are many tools out there, all with different pros and cons for Node.js performance tuning.
One main thing to consider is that even though you’re testing a Node.js application if you’re going to performance test from the outside world across a network, it doesn’t matter if your performance test tooling is written in Node.js.
For basic HTTP performance testing, I like Artillery a straightforward performance testing tool written in Node.js.
It’s also particularly good at running performance tests for API requests.
Artillery works by writing a configuration file that defines your load profile.
You tell Artillery which endpoints you want to request, at what rate, for what duration, etc. A basic test script looks like this:. Here, you’re requesting Artillery’s website for a 60-second duration with 20 new users arriving at the URL.
Then, to run the test, you simply execute:. Artillery will make as many requests to your application as you instructed it to. This is useful for building performance test profiles that mimic your production environment.
What do I mean by performance test profile? Let’s cover that now. A performance test profile, as above, is a definition of how your performance test will run.
You’ll want to mimic how your production traffic does or is expected to, behave, if possible to do accurate Node.js performance tuning.