Slide 1

Slide 1 text

NODE.JS SCALABILITY TIPS NODE.JS SCALABILITY TIPS Luciano Mammino ( ) @loige loige.link/node-adc Vijayawada - 2021-12-13 1

Slide 2

Slide 2 text

@loige Get the slides! πŸ‘‡ loige.link/node-adc 2

Slide 3

Slide 3 text

HELLO! 3

Slide 4

Slide 4 text

HELLO! πŸ‘‹ I'm Luciano ( πŸ•πŸ) 3

Slide 5

Slide 5 text

HELLO! πŸ‘‹ I'm Luciano ( πŸ•πŸ) Senior Architect @ fourTheorem (Dublin ) 3

Slide 6

Slide 6 text

HELLO! πŸ‘‹ I'm Luciano ( πŸ•πŸ) Senior Architect @ fourTheorem (Dublin ) nodejsdp.link πŸ“” Co-Author of Node.js Design Patterns πŸ‘‰ 3

Slide 7

Slide 7 text

HELLO! πŸ‘‹ I'm Luciano ( πŸ•πŸ) Senior Architect @ fourTheorem (Dublin ) nodejsdp.link πŸ“” Co-Author of Node.js Design Patterns πŸ‘‰ Let's connect! (blog) (twitter) (twitch) (github) loige.co @loige loige lmammino 3

Slide 8

Slide 8 text

WE ARE BUSINESS FOCUSED TECHNOLOGISTS THAT DELIVER. | | Accelerated Serverless AI as a Service Platform Modernisation We are hiring: do you want to ? work with us loige 4

Slide 9

Slide 9 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 5

Slide 10

Slide 10 text

πŸ›« TIP 1. ESTABLISH A BASELINE @loige 6

Slide 11

Slide 11 text

/?data=ciao πŸ‘‹ @loige Site: - Code: qrgen.lmammino.repl.co replit.com/@lmammino/QRGen 7

Slide 12

Slide 12 text

import { createServer } from 'http' import QRCode from '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(200, {'Content-Type': 'text/html'}) return res.end(`

To use this app you need to set the data querystrin

Try for example /?data=hello

`) } 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 16 17 18 @loige 8

Slide 13

Slide 13 text

import { createServer } from 'http' import QRCode from '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(200, {'Content-Type': 'text/html'}) return res.end(`

To use this app you need to set the data querystrin

Try for example /?data=hello

`) } 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 16 17 18 import { createServer } from 'http' import QRCode from 'qrcode' 1 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(` 9 10

To use this app you need to set the data querystrin 11

Try for example /?data=hello

12 13 `) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 @loige 8

Slide 14

Slide 14 text

import { createServer } from 'http' import QRCode from '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(200, {'Content-Type': 'text/html'}) return res.end(`

To use this app you need to set the data querystrin

Try for example /?data=hello

`) } 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 16 17 18 import { createServer } from 'http' import QRCode from 'qrcode' 1 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(` 9 10

To use this app you need to set the data querystrin 11

Try for example /?data=hello

12 13 `) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 createServer(function handler (req, res) { }).listen(8080) import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(` 9 10

To use this app you need to set the data querystrin 11

Try for example /?data=hello

12 13 `) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 18 @loige 8

Slide 15

Slide 15 text

import { createServer } from 'http' import QRCode from '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(200, {'Content-Type': 'text/html'}) return res.end(`

To use this app you need to set the data querystrin

Try for example /?data=hello

`) } 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 16 17 18 import { createServer } from 'http' import QRCode from 'qrcode' 1 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(` 9 10

To use this app you need to set the data querystrin 11

Try for example /?data=hello

12 13 `) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 createServer(function handler (req, res) { }).listen(8080) import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(` 9 10

To use this app you need to set the data querystrin 11

Try for example /?data=hello

12 13 `) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 18 const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 createServer(function handler (req, res) { 4 5 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(` 9 10

To use this app you need to set the data querystrin 11

Try for example /?data=hello

12 13 `) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 @loige 8

Slide 16

Slide 16 text

import { createServer } from 'http' import QRCode from '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(200, {'Content-Type': 'text/html'}) return res.end(`

To use this app you need to set the data querystrin

Try for example /?data=hello

`) } 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 16 17 18 import { createServer } from 'http' import QRCode from 'qrcode' 1 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(` 9 10

To use this app you need to set the data querystrin 11

Try for example /?data=hello

12 13 `) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 createServer(function handler (req, res) { }).listen(8080) import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(` 9 10

To use this app you need to set the data querystrin 11

Try for example /?data=hello

12 13 `) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 18 const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 createServer(function handler (req, res) { 4 5 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(` 9 10

To use this app you need to set the data querystrin 11

Try for example /?data=hello

12 13 `) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 if (!data) { res.writeHead(200, {'Content-Type': 'text/html'}) return res.end(`

To use this app you need to set the data querystrin

Try for example /?data=hello

`) } import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 7 8 9 10 11 12 13 14 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 @loige 8

Slide 17

Slide 17 text

import { createServer } from 'http' import QRCode from '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(200, {'Content-Type': 'text/html'}) return res.end(`

To use this app you need to set the data querystrin

Try for example /?data=hello

`) } 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 16 17 18 import { createServer } from 'http' import QRCode from 'qrcode' 1 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(` 9 10

To use this app you need to set the data querystrin 11

Try for example /?data=hello

12 13 `) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 createServer(function handler (req, res) { }).listen(8080) import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(` 9 10

To use this app you need to set the data querystrin 11

Try for example /?data=hello

12 13 `) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 18 const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 createServer(function handler (req, res) { 4 5 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(` 9 10

To use this app you need to set the data querystrin 11

Try for example /?data=hello

12 13 `) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 if (!data) { res.writeHead(200, {'Content-Type': 'text/html'}) return res.end(`

To use this app you need to set the data querystrin

Try for example /?data=hello

`) } import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 7 8 9 10 11 12 13 14 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 res.writeHead(200, { 'Content-Type': 'image/png' }) QRCode.toFileStream(res, data, { width: 300 }) import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(` 9 10

To use this app you need to set the data querystrin 11

Try for example /?data=hello

12 13 `) 14 } 15 16 17 }).listen(8080) 18 @loige 8

Slide 18

Slide 18 text

import { createServer } from 'http' import QRCode from '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(200, {'Content-Type': 'text/html'}) return res.end(`

To use this app you need to set the data querystrin

Try for example /?data=hello

`) } 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 16 17 18 import { createServer } from 'http' import QRCode from 'qrcode' 1 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(` 9 10

To use this app you need to set the data querystrin 11

Try for example /?data=hello

12 13 `) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 createServer(function handler (req, res) { }).listen(8080) import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(` 9 10

To use this app you need to set the data querystrin 11

Try for example /?data=hello

12 13 `) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 18 const url = new URL(req.url, 'http://localhost:8080') const data = url.searchParams.get('data') import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 createServer(function handler (req, res) { 4 5 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(` 9 10

To use this app you need to set the data querystrin 11

Try for example /?data=hello

12 13 `) 14 } 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 if (!data) { res.writeHead(200, {'Content-Type': 'text/html'}) return res.end(`

To use this app you need to set the data querystrin

Try for example /?data=hello

`) } import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 7 8 9 10 11 12 13 14 15 res.writeHead(200, { 'Content-Type': 'image/png' }) 16 QRCode.toFileStream(res, data, { width: 300 }) 17 }).listen(8080) 18 res.writeHead(200, { 'Content-Type': 'image/png' }) QRCode.toFileStream(res, data, { width: 300 }) import { createServer } from 'http' 1 import QRCode from 'qrcode' 2 3 createServer(function handler (req, res) { 4 const url = new URL(req.url, 'http://localhost:8080') 5 const data = url.searchParams.get('data') 6 if (!data) { 7 res.writeHead(200, {'Content-Type': 'text/html'}) 8 return res.end(` 9 10

To use this app you need to set the data querystrin 11

Try for example /?data=hello

12 13 `) 14 } 15 16 17 }).listen(8080) 18 import { createServer } from 'http' import QRCode from '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(200, {'Content-Type': 'text/html'}) return res.end(`

To use this app you need to set the data querystrin

Try for example /?data=hello

`) } 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 16 17 18 @loige 8

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

autocannon -c 200 --on-port /?data=ciao πŸ‘‹ -- 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 @loige 10

Slide 21

Slide 21 text

autocannon -c 200 --on-port /?data=ciao πŸ‘‹ -- 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 @loige 10

Slide 22

Slide 22 text

β›… TIP 1-BIS ALSO, FIND OUT YOUR CEILING @loige 11

Slide 23

Slide 23 text

import { createServer } from '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 @loige 12

Slide 24

Slide 24 text

import { createServer } from '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 res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('Hello World\n') import { createServer } from '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 }).listen(8080) 11 @loige 12

Slide 25

Slide 25 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 13

Slide 26

Slide 26 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 13

Slide 27

Slide 27 text

🍾 TIP 2. FIND YOUR BOTTLENECK @loige 14

Slide 28

Slide 28 text

Clinic.js clinicjs.org @loige 15

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

🎳 TIP 3. UNDERSTAND YOUR GOALS @loige 19

Slide 33

Slide 33 text

WHAT DO WE OPTIMIZE FOR? THROUGHPUT? MEMORY? LATENCY? @loige 20

Slide 34

Slide 34 text

πŸ‘ TIP 4. ALWAYS "OBSERVE" @loige 21

Slide 35

Slide 35 text

I MEAN, IN PRODUCTION! Logs - Metrics - Traces @loige 22

Slide 36

Slide 36 text

πŸš€ TIP 5. SCALE YOUR ARCHITECTURE @loige 23

Slide 37

Slide 37 text

PERFORMANCE != SCALABILITY @loige 24

Slide 38

Slide 38 text

HOW CAN WE SCALE A SYSTEM BY ADDING RESOURCES? @loige 25

Slide 39

Slide 39 text

THE " " SCALE CUBE x-axis cloning z-axis partitioning y-axis functional decomposition @loige 26

Slide 40

Slide 40 text

THE " " SCALE CUBE x-axis cloning z-axis partitioning y-axis functional decomposition @loige 26

Slide 41

Slide 41 text

27 Reverse proxy CLONING Inside the same server Load Balancer Using multiple server @loige

Slide 42

Slide 42 text

THE MODULE CLUSTER Master process Worker process Worker process Worker process 28 @loige

Slide 43

Slide 43 text

THE MODULE CLUSTER Master process Worker process Worker process Worker process 28 @loige

Slide 44

Slide 44 text

THE MODULE CLUSTER Master process Worker process Worker process Worker process 28 @loige

Slide 45

Slide 45 text

THE MODULE CLUSTER Master process Worker process Worker process Worker process 28 @loige

Slide 46

Slide 46 text

THE MODULE CLUSTER Master process Worker process Worker process Worker process 28 @loige

Slide 47

Slide 47 text

THE MODULE CLUSTER Master process Worker process Worker process Worker process 28 @loige

Slide 48

Slide 48 text

THE MODULE CLUSTER Master process Worker process Worker process Worker process 28 @loige

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

YOU COULD ALSO USE CHECK OUT ! WORKER THREADS PISCINA @loige 30

Slide 57

Slide 57 text

CLONING IS THE EASIEST STRATEGY TO SCALE A SERVICE... ... AS LONG AS YOUR APPLICATION IS "STATELESS" @loige 31

Slide 58

Slide 58 text

API Gateway FUNCTIONAL DECOMPOSITION a.k.a. "Micro-services" 32 /products /cart cart DB products DB @loige

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

NODE.JS IS GREAT FOR MICROSERVICES @loige 34

Slide 61

Slide 61 text

MICROSERVICES CAN ALSO HELP WITH SCALING THE ORGANISATION! @loige 35

Slide 62

Slide 62 text

MICROSERVICES ADD COMPLEXITY Observability Deployments Versioning Integration @loige 36

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

PARTITIONING IS GENERALLY USED TO SCALE DATABASES AND SAAS SOFTWARE GEOGRAPHICALLY @loige 38

Slide 65

Slide 65 text

SUMMARY @loige 39

Slide 66

Slide 66 text

SUMMARY πŸ›« Establish a baseline @loige 39

Slide 67

Slide 67 text

SUMMARY πŸ›« Establish a baseline 🍾 Find your bottleneck @loige 39

Slide 68

Slide 68 text

SUMMARY πŸ›« Establish a baseline 🍾 Find your bottleneck 🎳 Understand your goals @loige 39

Slide 69

Slide 69 text

SUMMARY πŸ›« Establish a baseline 🍾 Find your bottleneck 🎳 Understand your goals πŸ‘ Always "observe" @loige 39

Slide 70

Slide 70 text

SUMMARY πŸ›« Establish a baseline 🍾 Find your bottleneck 🎳 Understand your goals πŸ‘ Always "observe" πŸš€ Scale your architecture (cloning, decomposition & partitioning) @loige 39

Slide 71

Slide 71 text

☝ nodejsdp.link THANK YOU! Special thanks to , , , , , , , , , , , , , , , , , , , , , Icons and SVGs by Photo by on @StefanoAbalsamo @matteocollina @dagonzago @NullishCoalesce @DublinSvelte @KViglucci @gjohnson391 @lucamaraschi @laurekamalandua @giltayar @mrm8488 @adrirai @harafise @EugeneWare @Jauny @tlivings @michaelcfine @leojino @shahidontech @Lordoomer @zsadigov @dottorblaster freepik.com Jonas Kaiser Unsplash @loige loige.link/node-adc 40