Slide 1

Slide 1 text

Node.js Scalability Tips Luciano Mammino ( ) @loige loige.link/node-scale-city 2020-09-15 1

Slide 2

Slide 2 text

Hello, I am Luciano! Principal Software Engineer at FabFitFun Blog: Twitter: GitHub: loige.co @loige @lmammino nodejsdp.link/buy 2

Slide 3

Slide 3 text

Hello, I am Luciano! Principal Software Engineer at FabFitFun Blog: Twitter: GitHub: loige.co @loige @lmammino nodejsdp.link/buy CITYJS SPECIAL DISCOUNT: 20% OFF on Amazon.com - www.amazon.com/gp/mpc/A2L4T1QGT3DXYX 2

Slide 4

Slide 4 text

@loige Get the slides! loige.link/node-scale-city 3

Slide 5

Slide 5 text

Node.js + Scalability? @loige 4

Slide 6

Slide 6 text

@loige 5

Slide 7

Slide 7 text

@loige 6

Slide 8

Slide 8 text

@loige 6

Slide 9

Slide 9 text

@loige 6

Slide 10

Slide 10 text

@loige 6

Slide 11

Slide 11 text

@loige 7

Slide 12

Slide 12 text

@loige 7

Slide 13

Slide 13 text

@loige 7

Slide 14

Slide 14 text

@loige 7

Slide 15

Slide 15 text

@loige 7

Slide 16

Slide 16 text

"Scalability is the property of a system to handle a growing amount of work by adding resources to the system" — Wikipedia @loige 8

Slide 17

Slide 17 text

"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 @loige 9

Slide 18

Slide 18 text

Tip 1. Establish a baseline @loige 10

Slide 19

Slide 19 text

/?data=hello%20cityjs https://repl.it/@lmammino/QRGen @loige 11

Slide 20

Slide 20 text

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 }) }) .listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @loige 12

Slide 21

Slide 21 text

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 }) }) .listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const { createServer } = require('http') const { URL } = require('url') const QRCode = require('qrcode') 1 2 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 @loige 12

Slide 22

Slide 22 text

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 }) }) .listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const { createServer } = require('http') const { URL } = require('url') const QRCode = require('qrcode') 1 2 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 createServer(function handler (req, res) { }) .listen(8080) const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 14 15 @loige 12

Slide 23

Slide 23 text

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 }) }) .listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const { createServer } = require('http') const { URL } = require('url') const QRCode = require('qrcode') 1 2 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 createServer(function handler (req, res) { }) .listen(8080) const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 14 15 const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 createServer(function handler (req, res) { 5 6 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 @loige 12

Slide 24

Slide 24 text

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 }) }) .listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const { createServer } = require('http') const { URL } = require('url') const QRCode = require('qrcode') 1 2 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 createServer(function handler (req, res) { }) .listen(8080) const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 14 15 const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 createServer(function handler (req, res) { 5 6 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 if (!data) { res.writeHead(400) // bad request return res.end() } const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 8 9 10 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 @loige 12

Slide 25

Slide 25 text

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 }) }) .listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const { createServer } = require('http') const { URL } = require('url') const QRCode = require('qrcode') 1 2 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 createServer(function handler (req, res) { }) .listen(8080) const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 14 15 const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 createServer(function handler (req, res) { 5 6 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 if (!data) { res.writeHead(400) // bad request return res.end() } const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 8 9 10 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 res.writeHead(200, { 'Content-Type': 'image/png' }) QRCode.toFileStream(res, data, { width: 300 }) const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 12 13 }) 14 .listen(8080) 15 @loige 12

Slide 26

Slide 26 text

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 }) }) .listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const { createServer } = require('http') const { URL } = require('url') const QRCode = require('qrcode') 1 2 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 createServer(function handler (req, res) { }) .listen(8080) const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 14 15 const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 createServer(function handler (req, res) { 5 6 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 if (!data) { res.writeHead(400) // bad request return res.end() } const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 8 9 10 11 res.writeHead(200, { 'Content-Type': 'image/png' }) 12 QRCode.toFileStream(res, data, { width: 300 }) 13 }) 14 .listen(8080) 15 res.writeHead(200, { 'Content-Type': 'image/png' }) QRCode.toFileStream(res, data, { width: 300 }) const { createServer } = require('http') 1 const { URL } = require('url') 2 const QRCode = require('qrcode') 3 4 createServer(function handler (req, res) { 5 const url = new URL(req.url, 'http://localhost:8080') 6 const data = url.searchParams.get('data') 7 if (!data) { 8 res.writeHead(400) // bad request 9 return res.end() 10 } 11 12 13 }) 14 .listen(8080) 15 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 }) }) .listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @loige 12

Slide 27

Slide 27 text

autocannon -c 200 --on-port / -- node server.js wrk node server.js& wrk -t8 -c200 -d10s http://localhost:8080/ @loige 13

Slide 28

Slide 28 text

autocannon -c 200 --on-port /?data=hello%20cityjs -- node server.js Running 10s test @ http://localhost:8080/?data=hello%20cityjs 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 @loige 14

Slide 29

Slide 29 text

autocannon -c 200 --on-port /?data=hello%20cityjs -- node server.js Running 10s test @ http://localhost:8080/?data=hello%20cityjs 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 @loige 14

Slide 30

Slide 30 text

⛅ Tip 1-bis Also, find out your ceiling @loige 15

Slide 31

Slide 31 text

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 res.end() } }) .listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 @loige 16

Slide 32

Slide 32 text

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 res.end() } }) .listen(8080) 1 2 3 4 5 6 7 8 9 10 11 12 res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('Hello World\n') const { createServer } = require('http') 1 2 createServer((req, res) => { 3 if (req.method === 'GET' && req.url === '/') { 4 5 6 } else { 7 res.statusCode = 404 8 res.end() 9 } 10 }) 11 .listen(8080) 12 @loige 16

Slide 33

Slide 33 text

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 @loige 17

Slide 34

Slide 34 text

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 @loige 17

Slide 35

Slide 35 text

Tip 2. Find your bottleneck @loige 18

Slide 36

Slide 36 text

Clinic.js clinicjs.org @loige 19

Slide 37

Slide 37 text

clinic doctor --autocannon [ -c 200 '/?data=hello%20cityjs' ] -- node server.js @loige 20

Slide 38

Slide 38 text

clinic flame --autocannon [ -c 200 '/?data=hello%20cityjs' ] -- node server.js @loige 21

Slide 39

Slide 39 text

clinic bubble --autocannon [ -c 200 '/?data=hello%20cityjs' ] -- node server.js @loige 22

Slide 40

Slide 40 text

Tip 3. Understand your goals @loige 23

Slide 41

Slide 41 text

What do we optimize for? Throughput? Memory? Latency? @loige 24

Slide 42

Slide 42 text

Tip 4. Always "observe" @loige 25

Slide 43

Slide 43 text

I mean, in production! Logs - Metrics - Traces @loige 26

Slide 44

Slide 44 text

Tip 5. Scale your architecture @loige 27

Slide 45

Slide 45 text

Performance != Scalability @loige 28

Slide 46

Slide 46 text

How can we scale a system by adding resources? @loige 29

Slide 47

Slide 47 text

The " " Scale Cube x-axis cloning z-axis partitioning y-axis functional decomposition @loige 30

Slide 48

Slide 48 text

The " " Scale Cube x-axis cloning z-axis partitioning y-axis functional decomposition @loige 30

Slide 49

Slide 49 text

Cloning Reverse proxy 31 Inside the same server Load Balancer Using multiple server @loige

Slide 50

Slide 50 text

The module cluster Master process Worker process Worker process Worker process 32 @loige

Slide 51

Slide 51 text

The module cluster Master process Worker process Worker process Worker process 32 @loige

Slide 52

Slide 52 text

The module cluster Master process Worker process Worker process Worker process 32 @loige

Slide 53

Slide 53 text

The module cluster Master process Worker process Worker process Worker process 32 @loige

Slide 54

Slide 54 text

The module cluster Master process Worker process Worker process Worker process 32 @loige

Slide 55

Slide 55 text

The module cluster Master process Worker process Worker process Worker process 32 @loige

Slide 56

Slide 56 text

The module cluster Master process Worker process Worker process Worker process 32 @loige

Slide 57

Slide 57 text

const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 @loige 33

Slide 58

Slide 58 text

const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 3-4x req/sec (8 core) @loige 33

Slide 59

Slide 59 text

const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 3-4x req/sec (8 core) @loige 33

Slide 60

Slide 60 text

const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 const numCPUs = require('os').cpus().length const cluster = require('cluster') 1 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 3-4x req/sec (8 core) @loige 33

Slide 61

Slide 61 text

const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 const numCPUs = require('os').cpus().length const cluster = require('cluster') 1 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 if (cluster.isMaster) { } else { } const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 9 // Worker code 10 require('./server.js') 11 12 3-4x req/sec (8 core) @loige 33

Slide 62

Slide 62 text

const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 const numCPUs = require('os').cpus().length const cluster = require('cluster') 1 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 if (cluster.isMaster) { } else { } const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 9 // Worker code 10 require('./server.js') 11 12 // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 5 6 7 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 3-4x req/sec (8 core) @loige 33

Slide 63

Slide 63 text

const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 const numCPUs = require('os').cpus().length const cluster = require('cluster') 1 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 if (cluster.isMaster) { } else { } const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 9 // Worker code 10 require('./server.js') 11 12 // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 5 6 7 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 // Worker code require('./server.js') const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 10 11 } 12 3-4x req/sec (8 core) @loige 33

Slide 64

Slide 64 text

const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 const numCPUs = require('os').cpus().length const cluster = require('cluster') 1 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 if (cluster.isMaster) { } else { } const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 9 // Worker code 10 require('./server.js') 11 12 // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 5 6 7 8 } else { 9 // Worker code 10 require('./server.js') 11 } 12 // Worker code require('./server.js') const cluster = require('cluster') 1 const numCPUs = require('os').cpus().length 2 3 if (cluster.isMaster) { 4 // Fork workers 5 for (let i = 0; i < numCPUs; i++) { 6 cluster.fork() 7 } 8 } else { 9 10 11 } 12 const cluster = require('cluster') const numCPUs = require('os').cpus().length if (cluster.isMaster) { // Fork workers for (let i = 0; i < numCPUs; i++) { cluster.fork() } } else { // Worker code require('./server.js') } 1 2 3 4 5 6 7 8 9 10 11 12 3-4x req/sec (8 core) @loige 33

Slide 65

Slide 65 text

You could also use Check out ! Worker Threads piscina @loige 34

Slide 66

Slide 66 text

Cloning is the easiest strategy to scale a service... ... as long as your application is "Stateless" @loige 35

Slide 67

Slide 67 text

API Gateway Functional decomposition a.k.a. "Micro-services" 36 /products /cart cart DB products DB @loige

Slide 68

Slide 68 text

API Gateway Functional decomposition a.k.a. "Micro-services" 37 /products /cart Functional decomposition can also be combined with cloning! cart DB products DB @loige

Slide 69

Slide 69 text

Node.js is great for microservices @loige 38

Slide 70

Slide 70 text

Microservices can also help with scaling the organisation! @loige 39

Slide 71

Slide 71 text

Microservices add complexity Observability Deployments Versioning Integration @loige 40

Slide 72

Slide 72 text

Partitioning Service and Data Partitioning along Customer Boundaries Shard partitioning /products/[A-L]/ /products/[M-Z]/ DB 2 41 DB 1 @loige

Slide 73

Slide 73 text

Partitioning is generally used to scale databases and SaaS software geographically @loige 42

Slide 74

Slide 74 text

Summary @loige 43

Slide 75

Slide 75 text

Summary Establish a baseline @loige 43

Slide 76

Slide 76 text

Summary Establish a baseline Find your bottleneck @loige 43

Slide 77

Slide 77 text

Summary Establish a baseline Find your bottleneck Understand your goals @loige 43

Slide 78

Slide 78 text

Summary Establish a baseline Find your bottleneck Understand your goals Always "observe" @loige 43

Slide 79

Slide 79 text

Summary Establish a baseline Find your bottleneck Understand your goals Always "observe" Scale your architecture (cloning, decomposition & partitioning) @loige 43

Slide 80

Slide 80 text

Thank you! Special thanks to , , , , , , , , , , , , , , , , , , , , , Icons and SVGs by @StefanoAbalsamo @matteocollina @dagonzago @NullishCoalesce @DublinSvelte @KViglucci @gjohnson391 @lucamaraschi @laurekamalandua @giltayar @mrm8488 @adrirai @harafise @EugeneWare @Jauny @tlivings @michaelcfine @leojino @shahidontech @Lordoomer @zsadigov @dottorblaster freepik.com loige.link/node-scale-city @loige 44