From 8d79084a579bcb6495f8b57f35bbc4e453306dd7 Mon Sep 17 00:00:00 2001 From: Glen Keane Date: Wed, 20 Jul 2016 12:46:37 +0100 Subject: [PATCH 1/5] expose totalRequests made --- lib/httpClient.js | 1 + lib/run.js | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/httpClient.js b/lib/httpClient.js index efb9dfb2..c5616dbe 100644 --- a/lib/httpClient.js +++ b/lib/httpClient.js @@ -129,6 +129,7 @@ Client.prototype._doRequest = function (rpi) { this.resData[rpi].startTime = process.hrtime() this.conn.write(this.requestIterator.move()) this.timeoutTicker.reschedule(this.timeout) + this.emit('request') } else { this.paused = true } diff --git a/lib/run.js b/lib/run.js index 0d5d6d4a..0e4d81c9 100644 --- a/lib/run.js +++ b/lib/run.js @@ -58,6 +58,7 @@ function run (opts, cb) { let timeouts = 0 let totalBytes = 0 let totalRequests = 0 + let totalCompletedRequests = 0 let amount = opts.amount let stop = false let numRunning = opts.connections @@ -92,6 +93,7 @@ function run (opts, cb) { client.on('connError', onError) client.on('timeout', onTimeout) client.on('done', onDone) + client.on('request', () => totalRequests++) clients.push(client) } @@ -142,7 +144,7 @@ function run (opts, cb) { const interval = setInterval(() => { totalBytes += bytes - totalRequests += counter + totalCompletedRequests += counter requests.record(counter) throughput.record(bytes) counter = 0 @@ -155,9 +157,10 @@ function run (opts, cb) { let result = { title: opts.title, url: opts.url, - requests: histAsObj(requests, totalRequests), + requests: histAsObj(requests, totalCompletedRequests), latency: addPercentiles(latencies, histAsObj(latencies)), throughput: histAsObj(throughput, totalBytes), + totalRequests: totalRequests, errors: errors, timeouts: timeouts, duration: Math.round((Date.now() - startTime) / 1000), From df339593be7947710b26f85dcce9e8d35d9d3a7c Mon Sep 17 00:00:00 2001 From: Glen Keane Date: Wed, 20 Jul 2016 16:44:08 +0100 Subject: [PATCH 2/5] added tests and fixed bug with counting amount sent --- lib/httpClient.js | 2 +- lib/run.js | 5 ++++- test/run.test.js | 2 ++ test/runAmount.test.js | 12 +++++++++--- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/httpClient.js b/lib/httpClient.js index c5616dbe..385481d4 100644 --- a/lib/httpClient.js +++ b/lib/httpClient.js @@ -125,11 +125,11 @@ Client.prototype._doRequest = function (rpi) { this.destroy() return } + this.emit('request') this.resData[rpi].startTime = process.hrtime() this.conn.write(this.requestIterator.move()) this.timeoutTicker.reschedule(this.timeout) - this.emit('request') } else { this.paused = true } diff --git a/lib/run.js b/lib/run.js index 0e4d81c9..113ddfd6 100644 --- a/lib/run.js +++ b/lib/run.js @@ -92,9 +92,12 @@ function run (opts, cb) { client.on('response', onResponse) client.on('connError', onError) client.on('timeout', onTimeout) + client.on('request', () => { totalRequests++ }) client.on('done', onDone) - client.on('request', () => totalRequests++) clients.push(client) + + // we will miss the initial request emits because the client emits request on construction + totalRequests += url.pipelining < url.rate ? url.rate : url.pipelining } function distributeNums (x, i) { diff --git a/test/run.test.js b/test/run.test.js index e63c6d8f..9d1ae03d 100644 --- a/test/run.test.js +++ b/test/run.test.js @@ -31,6 +31,8 @@ test('run', (t) => { t.ok(result.requests.min, 'requests.min exists') t.ok(result.requests.max, 'requests.max exists') t.ok(result.requests.total >= result.requests.average * 2 / 100 * 95, 'requests.total exists') + t.ok(result.totalRequests, 'totalRequests exists') + t.ok(result.totalRequests >= result.requests.total, 'total requests made should be more than or equal to completed requests total') t.ok(result.throughput, 'throughput exists') t.ok(result.throughput.average, 'throughput.average exists') diff --git a/test/runAmount.test.js b/test/runAmount.test.js index d8cd135a..3b6fabdd 100644 --- a/test/runAmount.test.js +++ b/test/runAmount.test.js @@ -7,7 +7,7 @@ const timeoutServer = helper.startTimeoutServer() const server = helper.startServer() test('run should only send the expected number of requests', (t) => { - t.plan(7) + t.plan(10) let done = false @@ -18,7 +18,8 @@ test('run should only send the expected number of requests', (t) => { amount: 50146 }, (err, res) => { t.error(err) - t.equal(res.requests.total, 50146, 'results should match the amount') + t.equal(res.requests.total + res.timeouts, 50146, 'results should match the amount') + t.equal(res.totalRequests, 50146, 'totalRequests should match the amount') done = true }) @@ -33,6 +34,7 @@ test('run should only send the expected number of requests', (t) => { }, (err, res) => { t.error(err) t.equal(res.requests.total, 20, 'results should match max connection requests * connections') + t.equal(res.totalRequests, 20, 'totalRequests should match the expected amount') }) run({ @@ -42,11 +44,12 @@ test('run should only send the expected number of requests', (t) => { }, (err, res) => { t.error(err) t.equal(res.requests.total, 10, 'results should match max overall requests') + t.equal(res.totalRequests, 10, 'totalRequests should match the expected amount') }) }) test('should shutdown after all amounts timeout', (t) => { - t.plan(2) + t.plan(5) run({ url: `http://localhost:${timeoutServer.address().port}`, @@ -56,5 +59,8 @@ test('should shutdown after all amounts timeout', (t) => { }, (err, res) => { t.error(err) t.equal(res.errors, 10) + t.equal(res.timeouts, 10) + t.equal(res.totalRequests, 10, 'totalRequests should match the expected amount') + t.equal(res.requests.total, 0, 'total completed requests should be 0') }) }) From 3ecbe2e79ba0e407594d9791c78d30994e36ba59 Mon Sep 17 00:00:00 2001 From: Glen Keane Date: Wed, 20 Jul 2016 17:10:35 +0100 Subject: [PATCH 3/5] rename totalRequests to sent --- lib/run.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/run.js b/lib/run.js index 113ddfd6..c1de5ea4 100644 --- a/lib/run.js +++ b/lib/run.js @@ -163,7 +163,7 @@ function run (opts, cb) { requests: histAsObj(requests, totalCompletedRequests), latency: addPercentiles(latencies, histAsObj(latencies)), throughput: histAsObj(throughput, totalBytes), - totalRequests: totalRequests, + sent: totalRequests, errors: errors, timeouts: timeouts, duration: Math.round((Date.now() - startTime) / 1000), From d5fc3c60fd1338c542f89623d2ce5a0c4aa8c7a0 Mon Sep 17 00:00:00 2001 From: Glen Keane Date: Wed, 20 Jul 2016 19:10:39 +0100 Subject: [PATCH 4/5] fix tests --- test/run.test.js | 6 ++++-- test/runAmount.test.js | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/test/run.test.js b/test/run.test.js index 9d1ae03d..c4720b60 100644 --- a/test/run.test.js +++ b/test/run.test.js @@ -31,8 +31,8 @@ test('run', (t) => { t.ok(result.requests.min, 'requests.min exists') t.ok(result.requests.max, 'requests.max exists') t.ok(result.requests.total >= result.requests.average * 2 / 100 * 95, 'requests.total exists') - t.ok(result.totalRequests, 'totalRequests exists') - t.ok(result.totalRequests >= result.requests.total, 'total requests made should be more than or equal to completed requests total') + t.ok(result.sent, 'sent exists') + t.ok(result.sent >= result.requests.total, 'total requests made should be more than or equal to completed requests total') t.ok(result.throughput, 'throughput exists') t.ok(result.throughput.average, 'throughput.average exists') @@ -82,6 +82,8 @@ test('tracker.stop()', (t) => { t.ok(result.requests.min, 'requests.min exists') t.ok(result.requests.max, 'requests.max exists') t.ok(result.requests.total >= result.requests.average * 2 / 100 * 95, 'requests.total exists') + t.ok(result.sent, 'sent exists') + t.ok(result.sent >= result.requests.total, 'total requests made should be more than or equal to completed requests total') t.ok(result.throughput, 'throughput exists') t.ok(result.throughput.average, 'throughput.average exists') diff --git a/test/runAmount.test.js b/test/runAmount.test.js index 3b6fabdd..9f555ffb 100644 --- a/test/runAmount.test.js +++ b/test/runAmount.test.js @@ -19,7 +19,7 @@ test('run should only send the expected number of requests', (t) => { }, (err, res) => { t.error(err) t.equal(res.requests.total + res.timeouts, 50146, 'results should match the amount') - t.equal(res.totalRequests, 50146, 'totalRequests should match the amount') + t.equal(res.sent, 50146, 'totalRequests should match the amount') done = true }) @@ -34,7 +34,7 @@ test('run should only send the expected number of requests', (t) => { }, (err, res) => { t.error(err) t.equal(res.requests.total, 20, 'results should match max connection requests * connections') - t.equal(res.totalRequests, 20, 'totalRequests should match the expected amount') + t.equal(res.sent, 20, 'totalRequests should match the expected amount') }) run({ @@ -44,7 +44,7 @@ test('run should only send the expected number of requests', (t) => { }, (err, res) => { t.error(err) t.equal(res.requests.total, 10, 'results should match max overall requests') - t.equal(res.totalRequests, 10, 'totalRequests should match the expected amount') + t.equal(res.sent, 10, 'totalRequests should match the expected amount') }) }) @@ -60,7 +60,7 @@ test('should shutdown after all amounts timeout', (t) => { t.error(err) t.equal(res.errors, 10) t.equal(res.timeouts, 10) - t.equal(res.totalRequests, 10, 'totalRequests should match the expected amount') + t.equal(res.sent, 10, 'totalRequests should match the expected amount') t.equal(res.requests.total, 0, 'total completed requests should be 0') }) }) From 645eed1869ba5b18f26642eb61071835fda33864 Mon Sep 17 00:00:00 2001 From: Glen Keane Date: Thu, 21 Jul 2016 10:44:23 +0100 Subject: [PATCH 5/5] added hdr-histogram-percentiles-obj and moved 'sent' to 'requests' object --- lib/percentiles.js | 13 ------------- lib/progressTracker.js | 2 +- lib/run.js | 30 ++++-------------------------- package.json | 1 + test/run.test.js | 8 ++++---- test/runAmount.test.js | 8 ++++---- 6 files changed, 14 insertions(+), 48 deletions(-) delete mode 100644 lib/percentiles.js diff --git a/lib/percentiles.js b/lib/percentiles.js deleted file mode 100644 index 1bdc9caf..00000000 --- a/lib/percentiles.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict' - -const percentiles = [ - 50, - 75, - 90, - 99, - 99.9, - 99.99, - 99.999 -] - -module.exports = percentiles diff --git a/lib/progressTracker.js b/lib/progressTracker.js index 08618cee..e262f73c 100644 --- a/lib/progressTracker.js +++ b/lib/progressTracker.js @@ -6,7 +6,7 @@ const chalk = require('chalk') const prettyBytes = require('pretty-bytes') const xtend = require('xtend') const format = require('./format') -const percentiles = require('./percentiles') +const percentiles = require('hdr-histogram-percentiles-obj').percentiles const defaults = { // use stderr as its progressBar's default outputStream: process.stderr, diff --git a/lib/run.js b/lib/run.js index c1de5ea4..22188eae 100644 --- a/lib/run.js +++ b/lib/run.js @@ -5,8 +5,10 @@ const URL = require('url') const Histogram = require('native-hdr-histogram') const timestring = require('timestring') const Client = require('./httpClient') -const percentiles = require('./percentiles') const xtend = require('xtend') +const histUtil = require('hdr-histogram-percentiles-obj') +const histAsObj = histUtil.histAsObj +const addPercentiles = histUtil.addPercentiles const defaultOptions = { headers: {}, @@ -163,7 +165,6 @@ function run (opts, cb) { requests: histAsObj(requests, totalCompletedRequests), latency: addPercentiles(latencies, histAsObj(latencies)), throughput: histAsObj(throughput, totalBytes), - sent: totalRequests, errors: errors, timeouts: timeouts, duration: Math.round((Date.now() - startTime) / 1000), @@ -173,6 +174,7 @@ function run (opts, cb) { pipelining: opts.pipelining, 'non2xx': statusCodes[0] + statusCodes[2] + statusCodes[3] + statusCodes[4] } + result.requests.sent = totalRequests statusCodes.forEach((code, index) => { result[(index + 1) + 'xx'] = code }) tracker.emit('done', result) @@ -239,30 +241,6 @@ function run (opts, cb) { return tracker } // run -function histAsObj (hist, total) { - const result = { - average: Math.ceil(hist.mean() * 100) / 100, - stddev: Math.ceil(hist.stddev() * 100) / 100, - min: hist.min(), - max: hist.max() - } - - if (typeof total === 'number') { - result.total = total - } - - return result -} - -function addPercentiles (hist, result) { - percentiles.forEach((perc) => { - const key = ('p' + perc).replace('.', '') - result[key] = hist.percentile(perc) - }) - - return result -} - function noop () {} module.exports = run diff --git a/package.json b/package.json index aed45104..36eee90d 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "dependencies": { "chalk": "^1.1.3", "deep-extend": "^0.4.1", + "hdr-histogram-percentiles-obj": "^1.1.0", "http-parser-js": "^0.4.2", "minimist": "^1.2.0", "native-hdr-histogram": "^0.3.0", diff --git a/test/run.test.js b/test/run.test.js index c4720b60..51dac63e 100644 --- a/test/run.test.js +++ b/test/run.test.js @@ -31,8 +31,8 @@ test('run', (t) => { t.ok(result.requests.min, 'requests.min exists') t.ok(result.requests.max, 'requests.max exists') t.ok(result.requests.total >= result.requests.average * 2 / 100 * 95, 'requests.total exists') - t.ok(result.sent, 'sent exists') - t.ok(result.sent >= result.requests.total, 'total requests made should be more than or equal to completed requests total') + t.ok(result.requests.sent, 'sent exists') + t.ok(result.requests.sent >= result.requests.total, 'total requests made should be more than or equal to completed requests total') t.ok(result.throughput, 'throughput exists') t.ok(result.throughput.average, 'throughput.average exists') @@ -82,8 +82,8 @@ test('tracker.stop()', (t) => { t.ok(result.requests.min, 'requests.min exists') t.ok(result.requests.max, 'requests.max exists') t.ok(result.requests.total >= result.requests.average * 2 / 100 * 95, 'requests.total exists') - t.ok(result.sent, 'sent exists') - t.ok(result.sent >= result.requests.total, 'total requests made should be more than or equal to completed requests total') + t.ok(result.requests.sent, 'sent exists') + t.ok(result.requests.sent >= result.requests.total, 'total requests made should be more than or equal to completed requests total') t.ok(result.throughput, 'throughput exists') t.ok(result.throughput.average, 'throughput.average exists') diff --git a/test/runAmount.test.js b/test/runAmount.test.js index 9f555ffb..f121fef7 100644 --- a/test/runAmount.test.js +++ b/test/runAmount.test.js @@ -19,7 +19,7 @@ test('run should only send the expected number of requests', (t) => { }, (err, res) => { t.error(err) t.equal(res.requests.total + res.timeouts, 50146, 'results should match the amount') - t.equal(res.sent, 50146, 'totalRequests should match the amount') + t.equal(res.requests.sent, 50146, 'totalRequests should match the amount') done = true }) @@ -34,7 +34,7 @@ test('run should only send the expected number of requests', (t) => { }, (err, res) => { t.error(err) t.equal(res.requests.total, 20, 'results should match max connection requests * connections') - t.equal(res.sent, 20, 'totalRequests should match the expected amount') + t.equal(res.requests.sent, 20, 'totalRequests should match the expected amount') }) run({ @@ -44,7 +44,7 @@ test('run should only send the expected number of requests', (t) => { }, (err, res) => { t.error(err) t.equal(res.requests.total, 10, 'results should match max overall requests') - t.equal(res.sent, 10, 'totalRequests should match the expected amount') + t.equal(res.requests.sent, 10, 'totalRequests should match the expected amount') }) }) @@ -60,7 +60,7 @@ test('should shutdown after all amounts timeout', (t) => { t.error(err) t.equal(res.errors, 10) t.equal(res.timeouts, 10) - t.equal(res.sent, 10, 'totalRequests should match the expected amount') + t.equal(res.requests.sent, 10, 'totalRequests should match the expected amount') t.equal(res.requests.total, 0, 'total completed requests should be 0') }) })