|
| 1 | +const {execSync, spawn} = require('child_process') |
| 2 | +const fs = require('fs') |
| 3 | + |
| 4 | +const runCommand = command => execSync(command, {encoding: 'utf8'}).trim() |
| 5 | + |
| 6 | +const startServer = (middleware, isVersionTest) => { |
| 7 | + console.log(`Starting server with ${middleware} middleware layers...`) |
| 8 | + const server = spawn('node', ['benchmarks/middleware.js'], { |
| 9 | + env: { |
| 10 | + ...process.env, |
| 11 | + MW: middleware, |
| 12 | + NO_LOCAL_EXPRESS: isVersionTest |
| 13 | + }, |
| 14 | + stdio: 'inherit' |
| 15 | + }) |
| 16 | + |
| 17 | + return new Promise((resolve, reject) => { |
| 18 | + setTimeout(() => { |
| 19 | + try { |
| 20 | + execSync('curl -s http://127.0.0.1:3333') |
| 21 | + resolve(server) |
| 22 | + } catch (error) { |
| 23 | + server.kill() |
| 24 | + reject(new Error('Server failed to start.')) |
| 25 | + } |
| 26 | + }, 3000) |
| 27 | + }) |
| 28 | +} |
| 29 | + |
| 30 | +const runLoadTest = (url, connectionsList) => { |
| 31 | + return connectionsList.map(connections => { |
| 32 | + try { |
| 33 | + const output = runCommand(`wrk ${url} -d 3 -c ${connections} -t 8`) |
| 34 | + const reqSec = output.match(/Requests\/sec:\s+(\d+.\d+)/)?.[1] |
| 35 | + const latency = output.match(/Latency\s+(\d+.\d+)/)?.[1] |
| 36 | + return {connections, reqSec, latency} |
| 37 | + } catch (error) { |
| 38 | + console.error( |
| 39 | + `Error running load test for ${connections} connections:`, |
| 40 | + error.message |
| 41 | + ) |
| 42 | + return {connections, reqSec: 'N/A', latency: 'N/A'} |
| 43 | + } |
| 44 | + }) |
| 45 | +} |
| 46 | + |
| 47 | +const generateMarkdownTable = results => { |
| 48 | + const headers = `| Connections | Requests/sec | Latency |\n|-------------|--------------|---------|` |
| 49 | + const rows = results |
| 50 | + .map( |
| 51 | + r => `| ${r.connections} | ${r.reqSec || 'N/A'} | ${r.latency || 'N/A'} |` |
| 52 | + ) |
| 53 | + .join('\n') |
| 54 | + return `${headers}\n${rows}` |
| 55 | +} |
| 56 | + |
| 57 | +const cleanUp = () => { |
| 58 | + console.log('Cleaning up...') |
| 59 | + runCommand('npm uninstall express') |
| 60 | + runCommand('rm -rf package-lock.json node_modules') |
| 61 | +} |
| 62 | + |
| 63 | +const runTests = async ({ |
| 64 | + identifier, |
| 65 | + connectionsList, |
| 66 | + middlewareCounts, |
| 67 | + outputFile, |
| 68 | + isVersionTest = false |
| 69 | +}) => { |
| 70 | + if (isVersionTest) { |
| 71 | + console.log(`Installing Express v${identifier}...`) |
| 72 | + runCommand(`npm install express@${identifier}`) |
| 73 | + } else { |
| 74 | + console.log(`Checking out branch ${identifier}...`) |
| 75 | + runCommand(`git fetch origin ${identifier}`) |
| 76 | + runCommand(`git checkout ${identifier}`) |
| 77 | + runCommand('npm install') |
| 78 | + console.log('Installing deps...') |
| 79 | + } |
| 80 | + |
| 81 | + const resultsMarkdown = [ |
| 82 | + `\n\n# Load Test Results for ${isVersionTest ? `Express v${identifier}` : `Branch ${identifier}`}` |
| 83 | + ] |
| 84 | + |
| 85 | + for (const middlewareCount of middlewareCounts) { |
| 86 | + try { |
| 87 | + const server = await startServer(middlewareCount, isVersionTest) |
| 88 | + const results = runLoadTest( |
| 89 | + 'http://127.0.0.1:3333/?foo[bar]=baz', |
| 90 | + connectionsList |
| 91 | + ) |
| 92 | + server.kill() |
| 93 | + resultsMarkdown.push( |
| 94 | + `### Load test for ${middlewareCount} middleware layers\n\n${generateMarkdownTable(results)}` |
| 95 | + ) |
| 96 | + } catch (error) { |
| 97 | + console.error('Error in load test process:', error) |
| 98 | + } |
| 99 | + } |
| 100 | + |
| 101 | + fs.writeFileSync(outputFile, resultsMarkdown.join('\n\n')) |
| 102 | + cleanUp() |
| 103 | +} |
| 104 | + |
| 105 | +const compareBranches = async ({ |
| 106 | + prevBranch, |
| 107 | + currBranch, |
| 108 | + connectionsList, |
| 109 | + middlewareCounts, |
| 110 | +}) => { |
| 111 | + console.log(`Comparing branches: ${prevBranch} vs ${currBranch}`) |
| 112 | + await runTests({ |
| 113 | + identifier: prevBranch, |
| 114 | + connectionsList, |
| 115 | + middlewareCounts, |
| 116 | + outputFile: `benchmarks/results_${prevBranch}.md`, |
| 117 | + isVersionTest: false |
| 118 | + }) |
| 119 | + await runTests({ |
| 120 | + identifier: currBranch, |
| 121 | + connectionsList, |
| 122 | + middlewareCounts, |
| 123 | + outputFile: `benchmarks/results_${currBranch}.md`, |
| 124 | + isVersionTest: false |
| 125 | + }) |
| 126 | +} |
| 127 | + |
| 128 | +const compareVersions = async ({ |
| 129 | + prevVersion, |
| 130 | + currVersion, |
| 131 | + connectionsList, |
| 132 | + middlewareCounts, |
| 133 | +}) => { |
| 134 | + console.log( |
| 135 | + `Comparing versions: Express v${prevVersion} vs Express v${currVersion}` |
| 136 | + ) |
| 137 | + await runTests({ |
| 138 | + identifier: prevVersion, |
| 139 | + connectionsList, |
| 140 | + middlewareCounts, |
| 141 | + outputFile: `benchmarks/results_${prevVersion}.md`, |
| 142 | + isVersionTest: true |
| 143 | + }) |
| 144 | + await runTests({ |
| 145 | + identifier: currVersion, |
| 146 | + connectionsList, |
| 147 | + middlewareCounts, |
| 148 | + outputFile: `benchmarks/results_${currVersion}.md`, |
| 149 | + isVersionTest: true |
| 150 | + }) |
| 151 | +} |
| 152 | + |
| 153 | +const compareBranchAndVersion = async ({ |
| 154 | + branch, |
| 155 | + version, |
| 156 | + connectionsList, |
| 157 | + middlewareCounts, |
| 158 | +}) => { |
| 159 | + console.log(`Comparing branch ${branch} with Express version ${version}`) |
| 160 | + await runTests({ |
| 161 | + identifier: branch, |
| 162 | + connectionsList, |
| 163 | + middlewareCounts, |
| 164 | + outputFile: `benchmarks/results_${branch}.md`, |
| 165 | + isVersionTest: false |
| 166 | + }) |
| 167 | + await runTests({ |
| 168 | + identifier: version, |
| 169 | + connectionsList, |
| 170 | + middlewareCounts, |
| 171 | + outputFile: `benchmarks/results_${version}.md`, |
| 172 | + isVersionTest: true |
| 173 | + }) |
| 174 | +} |
| 175 | + |
| 176 | +const main = async () => { |
| 177 | + const connectionsList = [50, 100, 250] |
| 178 | + const middlewareCounts = [1, 10, 25, 50] |
| 179 | + const prevBranch = process.env.PREV_BRANCH |
| 180 | + const currBranch = process.env.CURR_BRANCH |
| 181 | + const prevVersion = process.env.PREV_VERSION |
| 182 | + const currVersion = process.env.CURR_VERSION |
| 183 | + const version = process.env.VERSION |
| 184 | + const branch = process.env.BRANCH |
| 185 | + |
| 186 | + if (prevBranch && currBranch) { |
| 187 | + await compareBranches({ |
| 188 | + prevBranch, |
| 189 | + currBranch, |
| 190 | + connectionsList, |
| 191 | + middlewareCounts, |
| 192 | + }) |
| 193 | + return |
| 194 | + } |
| 195 | + |
| 196 | + if (prevVersion && currVersion) { |
| 197 | + await compareVersions({ |
| 198 | + prevVersion, |
| 199 | + currVersion, |
| 200 | + connectionsList, |
| 201 | + middlewareCounts, |
| 202 | + }) |
| 203 | + return |
| 204 | + } |
| 205 | + |
| 206 | + if (branch && version) { |
| 207 | + await compareBranchAndVersion({ |
| 208 | + branch, |
| 209 | + version, |
| 210 | + connectionsList, |
| 211 | + middlewareCounts, |
| 212 | + }) |
| 213 | + return |
| 214 | + } |
| 215 | + |
| 216 | + console.error( |
| 217 | + 'Invalid input combination. Provide either two branches, two versions, or one branch and one version.' |
| 218 | + ) |
| 219 | + process.exit(1) |
| 220 | +} |
| 221 | + |
| 222 | +main() |
0 commit comments