├── .gitignore ├── package.json ├── readme.md └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-concurrency", 3 | "version": "1.0.1", 4 | "description": "A Heroku Example for WEB_CONCURRENCY", 5 | "repository": "https://github.com/heroku-examples/node-concurrency.git", 6 | "readme": "https://github.com/heroku-examples/node-concurrency/blob/main/readme.md", 7 | "main": "server.js", 8 | "scripts": { 9 | "start": "node server.js" 10 | }, 11 | "author": "Hunter Loftis ", 12 | "license": "ISC", 13 | "engines": { 14 | "node": "10.15.0", 15 | "npm": "6.4.1" 16 | }, 17 | "dependencies": { 18 | "express": "^4.16.4", 19 | "throng": "^4.0.0" 20 | } 21 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Load Testing 2 | 3 | ``` 4 | $ heroku addons:create blazemeter 5 | $ heroku addons:open blazemeter 6 | ``` 7 | 8 | Then set up a URL test to benchmark different classes of apps: 9 | 10 | - `/cpu` for CPU-bound apps 11 | - `/io` for IO-bound apps 12 | - `/memory` for memory-bound apps 13 | - `/` for a no-op route 14 | 15 | This yields a hello-world type of baseline. The CPU-bound test does a little bit of computation, and then responds. The IO-bound test simulates waiting for a resource for 300ms, then responds. The memory-bound test generates a new 1 MB string for each response. The no-op route does essentially no work and generates the best performance you can expect with the given service and network configuration. 16 | 17 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const throng = require('throng') 2 | 3 | const WORKERS = process.env.WEB_CONCURRENCY || 1 4 | const PORT = process.env.PORT || 3000 5 | 6 | throng({ 7 | workers: WORKERS, 8 | lifetime: Infinity 9 | }, start) 10 | 11 | function start() { 12 | const crypto = require('crypto') 13 | const express = require('express') 14 | const app = express() 15 | 16 | app 17 | .get('/cpu', cpuBound) 18 | .get('/memory', memoryBound) 19 | .get('/io', ioBound) 20 | .get('/', hello) 21 | .listen(PORT, onListen) 22 | 23 | function hello(req, res, next) { 24 | res.send('Hello, world') 25 | } 26 | 27 | function cpuBound(req, res, next) { 28 | const key = Math.random() < 0.5 ? 'ninjaturtles' : 'powerrangers' 29 | const hmac = crypto.createHmac('sha512WithRSAEncryption', key) 30 | const date = Date.now() + '' 31 | hmac.setEncoding('base64') 32 | hmac.end(date, () => res.send('A hashed date for you! ' + hmac.read())) 33 | } 34 | 35 | function memoryBound(req, res, next) { 36 | const large = Buffer.alloc(10 * 1024 * 1024, 'X') 37 | setTimeout(() => { 38 | const len = large.length // close over the Buffer for 1s to try to foil V8's optimizations and bloat memory 39 | console.log(len) 40 | }, 1000).unref() 41 | res.send('Allocated 10 MB buffer') 42 | } 43 | 44 | function ioBound(req, res, next) { 45 | setTimeout(function SimulateDb() { 46 | res.send('Got response from fake db!') 47 | }, 300).unref() 48 | } 49 | 50 | function onListen() { 51 | console.log('Listening on', PORT) 52 | } 53 | } 54 | --------------------------------------------------------------------------------