Repository: mcollina/autocannon (JavaScript)

Issue:
reqError callback throws nothing
```js

  function finishedBench() {
    console.log('finished benchmarking');
    // console.log(res);
    // console.log('finished bench', err, res);
  }

  const config = {
    url: 'http://localhost:3001/5968537fe66a6431eaa81ff8/5987c22fcea4da29f1f97b29',
    amount: 54000,
    timeout: 5400,
    connections: 3000,
  };
  const instance = autocannon(config, finishedBench);

  instance.on('reqError', (...args) => {
    console.log('---------------------');
    console.log(args);
    console.log('---------------------');
  });
```
Managed to trigger some errors.

but this is what i seen on terminal

```sh
...
[]
---------------------
---------------------
[]
---------------------
---------------------
[]
---------------------
---------------------
[]
...
```
it seems in trigger the **reqError** event, but it misses the "details" of the error.


but when i adjust the config to 
```js
  const config = {
    url: 'http://localhost:3001/5968537fe66a6431eaa81ff8/5987c22fcea4da29f1f97b29',
    amount: 54000,
    timeout: 5400,
    connections: 2000,
  };
```
there is no **reqError** event emitted. :+1: 

Questions

1. is the 3k concurrent connections quite suffocating to my app?
2. why the reqError event is throwing empty?
3. on connections,
` 
 -c/--connections NUM
        The number of concurrent connections to use. default: 10.
`, does this mean that the autocannon will try its very best to create the connections and fire them simultenously to my app?
4. im developing an app that will handle 8M requests  and generate 8M records at least per day, what will be the ideal configurations/settings i must use against it?


Discussion:
Currently `'reqError'` does not emit an error, see: https://github.com/mcollina/autocannon/blob/34e23d6ffd46fcc457a28eda7d05952fd0b3b33b/lib/run.js#L210-L222.

It would be good to have it, would you like to send a PR?

As to your questions:

1. very likely, you might also be creating congestion to the TCP layer of your OS and hit the total number of file descriptors and/or other limits. Check the if the server is running at 100% of CPU.
2. see above
3. yes
4. a day timeframe is too wide. What will be your peak? 8M in 1 minutes (it's still less than a day :))?  Or 100 req/s?
> a day timeframe is too wide. What will be your peak? 8M in 1 minutes (it's still less than a day :))? Or 100 req/s?

i mean the **100 req/s**.

> 2. see above

i think this is exciting, first time though. Ill try.



Patch:
diff --git a/lib/run.js b/lib/run.js
index 5000fd97..52be72db 100644
--- a/lib/run.js
+++ b/lib/run.js
@@ -217,15 +217,16 @@ function run (opts, cb) {
       counter++
     }
 
-    function onError () {
-      for (let i = 0; i < opts.pipelining; i++) tracker.emit('reqError')
+    function onError (error) {
+      for (let i = 0; i < opts.pipelining; i++) tracker.emit('reqError', error)
       errors++
       if (opts.bailout && errors >= opts.bailout) stop = true
     }
 
     // treat a timeout as a special type of error
     function onTimeout () {
-      for (let i = 0; i < opts.pipelining; i++) tracker.emit('reqError')
+      let error = new Error('request timed out')
+      for (let i = 0; i < opts.pipelining; i++) tracker.emit('reqError', error)
       errors++
       timeouts++
       if (opts.bailout && errors >= opts.bailout) stop = true


Test patch:
diff --git a/test/helper.js b/test/helper.js
index 59a8c80d..29cf33ff 100644
--- a/test/helper.js
+++ b/test/helper.js
@@ -54,6 +54,21 @@ function startTimeoutServer () {
   return server
 }
 
+// this server destroys the socket on connection, should result in ECONNRESET
+function startSocketDestroyingServer () {
+  const server = http.createServer(handle)
+
+  function handle (req, res) {
+    res.destroy()
+    server.close()
+  }
+
+  server.listen(0)
+  server.unref()
+
+  return server
+}
+
 // this server won't reply to requests
 function startHttpsServer () {
   const options = {
@@ -116,6 +131,7 @@ function startTlsServer () {
 
 module.exports.startServer = startServer
 module.exports.startTimeoutServer = startTimeoutServer
+module.exports.startSocketDestroyingServer = startSocketDestroyingServer
 module.exports.startHttpsServer = startHttpsServer
 module.exports.startTrailerServer = startTrailerServer
 module.exports.startTlsServer = startTlsServer
diff --git a/test/run.test.js b/test/run.test.js
index efe233a2..f25afadc 100644
--- a/test/run.test.js
+++ b/test/run.test.js
@@ -344,3 +344,45 @@ test('run will exclude non 2xx stats from latency and throughput averages if exc
     t.end()
   })
 })
+
+test('tracker will emit reqError with error message on timeout', (t) => {
+  t.plan(2)
+
+  const server = helper.startTimeoutServer()
+
+  let tracker = run({
+    url: `http://localhost:${server.address().port}`,
+    connections: 1,
+    duration: 5,
+    timeout: 2,
+    bailout: 1,
+    excludeErrorStats: true
+  })
+
+  tracker.once('reqError', (err) => {
+    t.type(err, Error, 'reqError should pass an Error to listener')
+    t.equal(err.message, 'request timed out', 'error should indicate timeout')
+    tracker.stop()
+  })
+})
+
+test('tracker will emit reqError with error message on error', (t) => {
+  t.plan(2)
+
+  const server = helper.startSocketDestroyingServer()
+
+  let tracker = run({
+    url: `http://localhost:${server.address().port}`,
+    connections: 10,
+    duration: 15,
+    method: 'POST',
+    body: 'hello',
+    excludeErrorStats: true
+  })
+
+  tracker.once('reqError', (err) => {
+    t.type(err, Error, 'reqError should pass an Error to listener')
+    t.ok(err.message, 'err.message should have a value')
+    tracker.stop()
+  })
+})