UNPKG

loadtest

Version:

Run load tests for your web application. Mostly ab-compatible interface, with an option to force requests per second. Includes an API for automated load testing.

168 lines (154 loc) 5.15 kB
/** * Result of a load test. */ export class Result { constructor() { this.url = null this.cores = 0 this.maxRequests = 0 this.maxSeconds = 0 this.concurrency = 0 this.agent = null this.requestsPerSecond = null this.clients = 0 this.startTimeMs = Number.MAX_SAFE_INTEGER this.stopTimeMs = 0 this.elapsedSeconds = 0 this.totalRequests = 0 this.totalErrors = 0 this.totalTimeSeconds = 0 this.accumulatedMs = 0 this.maxLatencyMs = 0 this.minLatencyMs = Number.MAX_SAFE_INTEGER this.errorCodes = {} this.histogramMs = {} } compute(latency) { const options = latency.options this.url = options.url this.cores = options.cores this.maxRequests = parseInt(options.maxRequests) this.maxSeconds = parseInt(options.maxSeconds) this.concurrency = parseInt(options.concurrency) this.clients = latency.clients if (options.tcp) { this.agent = 'tcp' } else if (options.agentKeepAlive) { this.agent = 'keepalive' } else { this.agent = 'none' } this.requestsPerSecond = parseInt(options.requestsPerSecond) this.startTimeMs = Number(latency.startTimeNs / 1000000n) this.stopTimeMs = Number(latency.stopTimeNs / 1000000n) this.totalRequests = latency.totalRequests this.totalErrors = latency.totalErrors this.accumulatedMs = latency.totalTime this.maxLatencyMs = latency.maxLatencyMs this.minLatencyMs = latency.minLatencyMs this.errorCodes = latency.errorCodes this.histogramMs = latency.histogramMs this.computeDerived() } computeDerived() { this.elapsedSeconds = (this.stopTimeMs - this.startTimeMs) / 1000 this.totalTimeSeconds = this.elapsedSeconds // backwards compatibility const meanTime = this.accumulatedMs / this.totalRequests this.meanLatencyMs = Math.round(meanTime * 10) / 10 this.effectiveRps = Math.round(this.totalRequests / this.elapsedSeconds) this.rps = this.effectiveRps // backwards compatibility this.computePercentiles() } computePercentiles() { this.percentiles = { 50: false, 90: false, 95: false, 99: false }; let counted = 0; for (let ms = 0; ms <= this.maxLatencyMs; ms++) { if (!this.histogramMs[ms]) { continue; } counted += this.histogramMs[ms]; const percent = counted / this.totalRequests * 100; Object.keys(this.percentiles).forEach(percentile => { if (!this.percentiles[percentile] && percent > percentile) { this.percentiles[percentile] = ms; } }); } } combine(result) { // configuration this.url = this.url || result.url this.cores += 1 this.maxRequests += result.maxRequests this.maxSeconds = this.maxSeconds || result.maxSeconds this.concurrency = this.concurrency || result.concurrency this.agent = this.agent || result.agent this.requestsPerSecond += result.requestsPerSecond || 0 this.clients += result.clients // result this.startTimeMs = Math.min(this.startTimeMs, result.startTimeMs) this.stopTimeMs = Math.max(this.stopTimeMs, result.stopTimeMs) this.totalRequests += result.totalRequests this.totalErrors += result.totalErrors this.accumulatedMs += result.accumulatedMs this.maxLatencyMs = Math.max(this.maxLatencyMs, result.maxLatencyMs) this.minLatencyMs = Math.min(this.minLatencyMs, result.minLatencyMs) this.combineMap(this.errorCodes, result.errorCodes) this.combineMap(this.histogramMs, result.histogramMs) this.computeDerived() } combineMap(originalMap, addedMap) { for (const key in {...originalMap, ...addedMap}) { if (!originalMap[key]) { originalMap[key] = 0 } if (addedMap[key]) { originalMap[key] += addedMap[key] } } } /** * Show result of a load test. */ show() { console.info(''); console.info('Target URL: %s', this.url); if (this.maxRequests) { console.info('Max requests: %s', this.maxRequests); } else if (this.maxSeconds) { console.info('Max time (s): %s', this.maxSeconds); } if (this.requestsPerSecond) { console.info('Target rps: %s', this.requestsPerSecond); } console.info('Concurrent clients: %s', this.clients) if (this.cores) { console.info('Running on cores: %s', this.cores); } console.info('Agent: %s', this.agent); console.info(''); console.info('Completed requests: %s', this.totalRequests); console.info('Total errors: %s', this.totalErrors); console.info('Total time: %s s', this.elapsedSeconds); console.info('Mean latency: %s ms', this.meanLatencyMs); console.info('Effective rps: %s', this.effectiveRps); console.info(''); console.info('Percentage of requests served within a certain time'); Object.keys(this.percentiles).forEach(percentile => { console.info(' %s% %s ms', percentile, this.percentiles[percentile]); }); console.info(' 100% %s ms (longest request)', this.maxLatencyMs); if (this.totalErrors) { console.info(''); Object.keys(this.errorCodes).forEach(errorCode => { const padding = ' '.repeat(errorCode.length < 4 ? 4 - errorCode.length : 1); console.info(' %s%s: %s errors', padding, errorCode, this.errorCodes[errorCode]); }); } } }