Node.js Scalability Tips

Luciano Mammino (@loige)

πŸ‘‹ Hello, I am Luciano!

Cloud Architect and Fullstack developer

Β Blog:

Β Twitter: @loige

Β GitHub: @lmamminoΒ 

Get the slides!

"A service is said to be scalable if when we increase the resources in a system, it results in increased performance in a manner proportional to resources added"

β€” Werner Vogels

Tip 1.
Establish a baseline

const { createServer } = require('http')
const { URL } = require('url')
const QRCode = require('qrcode')

createServer(function handler (req, res) {
  const url = new URL(req.url, 'http://localhost:8080')
  const data = url.searchParams.get('data')
  if (!data) {
    res.writeHead(400) // bad request
    return res.end()
  res.writeHead(200, { 'Content-Type': 'image/png' })
  QRCode.toFileStream(res, data, { width: 300 })
autocannon -c 200 --on-port / -- node server.js
node server.js&
wrk -t8 -c200 -d10s http://localhost:8080/
autocannon -c 200 --on-port /?data=hello%20cityjs -- node server.js
Running 10s test @ http://localhost:8080/?data=ciaoπŸ‘‹
200 connections

β”‚ Stat    β”‚ 2.5%    β”‚ 50%     β”‚ 97.5%   β”‚ 99%     β”‚ Avg        β”‚ Stdev   β”‚ Max        β”‚
β”‚ Latency β”‚ 1899 ms β”‚ 1951 ms β”‚ 2053 ms β”‚ 2054 ms β”‚ 1964.92 ms β”‚ 99.9 ms β”‚ 3364.03 ms β”‚
β”‚ Stat      β”‚ 1%  β”‚ 2.5% β”‚ 50%     β”‚ 97.5%  β”‚ Avg    β”‚ Stdev  β”‚ Min     β”‚
β”‚ Req/Sec   β”‚ 0   β”‚ 0    β”‚ 30      β”‚ 199    β”‚ 99.5   β”‚ 94.27  β”‚ 30      β”‚
β”‚ Bytes/Sec β”‚ 0 B β”‚ 0 B  β”‚ 50.7 kB β”‚ 336 kB β”‚ 168 kB β”‚ 159 kB β”‚ 50.7 kB β”‚

Req/Bytes counts sampled once per second.

995 requests in 10.08s, 1.68 MB read

Tip 1-bis
Also, find out your ceiling

const { createServer } = require('http')

createServer((req, res) => {
  if (req.method === 'GET' && req.url === '/') {
    res.writeHead(200, { 'Content-Type': 'text/plain' })
    res.end('Hello World\n')
  } else {
    res.statusCode = 404
autocannon -c 200 --on-port / -- node server.js
Running 10s test @ http://localhost:8080/
200 connections

β”‚ Stat    β”‚ 2.5% β”‚ 50%  β”‚ 97.5% β”‚ 99%   β”‚ Avg     β”‚ Stdev   β”‚ Max      β”‚
β”‚ Latency β”‚ 3 ms β”‚ 5 ms β”‚ 11 ms β”‚ 14 ms β”‚ 5.51 ms β”‚ 2.71 ms β”‚ 80.63 ms β”‚
β”‚ Stat      β”‚ 1%      β”‚ 2.5%    β”‚ 50%    β”‚ 97.5%   β”‚ Avg     β”‚ Stdev   β”‚ Min     β”‚
β”‚ Req/Sec   β”‚ 21087   β”‚ 21087   β”‚ 34623  β”‚ 35487   β”‚ 33258.4 β”‚ 4107.01 β”‚ 21077   β”‚
β”‚ Bytes/Sec β”‚ 3.29 MB β”‚ 3.29 MB β”‚ 5.4 MB β”‚ 5.54 MB β”‚ 5.19 MB β”‚ 641 kB  β”‚ 3.29 MB β”‚

Req/Bytes counts sampled once per second.

333k requests in 10.1s, 51.9 MB read

Tip 2.
Find your bottleneck

clinic doctor --autocannon [ -c 200 '/?data=ciaoπŸ‘‹' ] -- node server.js
clinic flame --autocannon [ -c 200 '/?data=ciaoπŸ‘‹' ] -- node server.js
clinic bubble --autocannon [ -c 200 '/?data=ciaoπŸ‘‹' ] -- node server.js

Tip 3.
Understand your goals

What do we optimize for?




Tip 4.
Always "observe"

I mean, in production!

Logs - Metrics - Traces

Tip 5.
Scale your architecture

Performance != Scalability

How can we scale a system
by adding resources?

The "Scale Cube"






functional decomposition


Inside the same server

Load Balancer

Using multiple server

Reverse proxy

The cluster module

Master process

Worker process

Worker process

Worker process

const cluster = require('cluster')
const numCPUs = require('os').cpus().length

if (cluster.isMaster) {
  // Fork workers
  for (let i = 0; i < numCPUs; i++) {
} else {
  // Worker code

3-4x req/sec

(8 core)

You could also use Worker Threads

Check out piscina!

Cloning is the easiest strategy to scale a service...


... as long as your application is "Stateless"

Functional decomposition

a.k.a. "Micro-services"

API Gateway



products DB

cart DB

Functional decomposition

a.k.a. "Micro-services"

API Gateway



Functional decomposition can also be combined with cloning!

products DB

cart DB

Node.js is great for microservices

Microservices can also help with
scaling the organisation!

Microservices add complexity

  • Observability
  • Deployments
  • Versioning
  • Integration


Service and Data Partitioning along Customer Boundaries

Shard partitioning



DB 1

DB 2

Partitioning is generally used
to scale databases

SaaS software geographically


πŸ›« Establish a baseline

🍾 Find your bottleneck

🎳 Understand your goals

πŸ‘ Always "observe"

πŸš€ Scale your architecture
(cloning, decomposition & partitioning)

Thank you!

Node.js: scalability tips - Codemotion Online 2020

By Luciano Mammino

Node.js: scalability tips - Codemotion Online 2020

You finally built that amazing start-up idea you had in mind for years and you did it using Node.js! That's Great! You just launched it on Hacker News and you are very happy and proud... but now more and more people are using it and you start to have a nasty fear that Node.js won't scale because you now... it's single-threaded! Is your project doomed now? Do you have to invest your time on rewriting it in something like C++ or maybe Rust or even Go? You'd rather invest your time on adding valuable features for your users rather than learning a new language and rewriting everything from scratch, but what if nothing works anymore? And... by the way, what the heck "single-threaded" really means?! Fear no more, dear fellow developer! In this talk, we will discuss the architecture of Node.js going through its strengths and its weaknesses. We will then talk about scalability and I will share some valuable tips and tricks to make your Node.js app scale! Spoiler alert: you probably won't need Go or Rust :)

  • 483
Loading comments...

More from Luciano Mammino