loadtest 8.0.1 → 8.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,21 +1,56 @@
1
1
  import {loadTest, startServer} from '../index.js'
2
+ const cluster = await import('cluster')
3
+ import {getHalfCores} from '../lib/cluster.js'
2
4
 
3
- const port = 7359;
5
+ const port = 7360;
4
6
  const serverOptions = {port}
5
7
 
8
+ if (cluster.isPrimary) {
9
+ await runTcpPerformanceTest()
10
+ } else {
11
+ await runServer()
12
+ }
6
13
 
7
14
  async function runTcpPerformanceTest() {
8
- const server = await startServer(serverOptions)
15
+ const workers = await startWorkers()
9
16
  const options = {
10
17
  url: `http://localhost:${port}`,
11
18
  method: 'GET',
12
19
  tcp: true,
20
+ concurrency: 10,
13
21
  };
14
22
  const result = await loadTest(options)
15
- await server.close()
16
- console.log(`Requests received: ${server.totalRequests}`)
17
23
  result.show()
24
+ console.log(`Test finished; closing server on ${workers.length} cores`)
25
+ await stopServer(workers)
26
+ }
27
+
28
+ function startWorkers() {
29
+ return new Promise(resolve => {
30
+ const cores = getHalfCores()
31
+ const workers = []
32
+ for (let i = 0; i < cores; i++) {
33
+ const worker = cluster.fork()
34
+ worker.on('message', async () => {
35
+ workers.push(worker)
36
+ if (workers.length != cores) {
37
+ return
38
+ }
39
+ console.log(`Server started on ${workers.length} cores`)
40
+ return resolve(workers)
41
+ })
42
+ }
43
+ })
18
44
  }
19
45
 
20
- await runTcpPerformanceTest()
46
+ function stopServer(workers) {
47
+ for (const worker of workers) {
48
+ worker.kill('SIGTERM')
49
+ }
50
+ }
51
+
52
+ async function runServer() {
53
+ await startServer(serverOptions)
54
+ process.send('server ready')
55
+ }
21
56
 
@@ -1,7 +1,10 @@
1
- # TCP Sockets Performance
1
+ # Load Testing with TCP Sockets
2
2
 
3
- To improve performance the author has tried out using raw TCP sockets
4
- using the [net module](https://nodejs.org/api/net.html),
3
+ The `loadtest` module has impressive performance,
4
+ and it has got better during the years as the Node.js core improves.
5
+ Heavily inspired by `autocannon`,
6
+ the author has tried out using raw TCP sockets to improve performance.
7
+ They use the [net module](https://nodejs.org/api/net.html)
5
8
  instead of the [HTTP module](https://nodejs.org/api/http.html).
6
9
  This is the story of how it went.
7
10
 
@@ -113,8 +116,9 @@ Keep-alive cannot be used with `ab` as far as the author knows.
113
116
 
114
117
  #### Autocannon
115
118
 
116
- The [autocannon](https://www.npmjs.com/package/autocannon) package uses by default
117
- 10 concurrent connections with keep-alive enabled:
119
+ Next we will try out [`autocannon`](https://www.npmjs.com/package/autocannon),
120
+ the package that actually inspired this approach.
121
+ `autocannon` uses by default 10 concurrent connections with keep-alive enabled:
118
122
 
119
123
  ```console
120
124
  $ autocannon --version
@@ -195,10 +199,10 @@ We are around **20 krps**.
195
199
  Again quite far from the 57 krps by `autocannon`;
196
200
  close to `ab` but it doesn't use keep-alive so the comparison is meaningless.
197
201
 
198
- ### Proof of Concept
202
+ ### Proof of Concept: Barebones
199
203
 
200
204
  For the first implementation we want to learn if the bare sockets implementation is worth the time.
201
- In this naïve implementation we open the socket,
205
+ In this naïve barebones implementation we open the socket,
202
206
  send a short canned request without taking into account any parameters or headers:
203
207
 
204
208
  ```js
package/lib/parser.js CHANGED
@@ -1,4 +1,3 @@
1
- import microprofiler from 'microprofiler'
2
1
 
3
2
  const bodySeparator = '\r\n\r\n'
4
3
  const lineSeparator = '\r\n'
@@ -36,13 +35,10 @@ export class Parser {
36
35
  // cannot parse yet
37
36
  return
38
37
  }
39
- //const start1 = microprofiler.start()
40
38
  this.packetInfo = new PacketInfo(this.pending.length, division)
41
39
  const messageHeader = this.pending.subarray(0, division)
42
40
  const messageBody = this.pending.subarray(division + 4)
43
41
  this.parseFirstLine(messageHeader)
44
- //microprofiler.measureFrom(start1, 'parse first line', 100000)
45
- //const start2 = microprofiler.start()
46
42
  const key = this.packetInfo.getKey()
47
43
  const existing = packetInfos.get(key)
48
44
  if (existing && this.packetInfo.isDuplicated(existing)) {
@@ -52,10 +48,7 @@ export class Parser {
52
48
  }
53
49
  packetInfos.set(key, this.packetInfo)
54
50
  this.parseHeaders(messageHeader)
55
- //microprofiler.measureFrom(start2, 'parse headers', 100000)
56
- const start3 = microprofiler.start()
57
51
  this.parseBody(messageBody)
58
- microprofiler.measureFrom(start3, 'parse body', 100000)
59
52
  }
60
53
 
61
54
  parseFirstLine(messageHeader) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loadtest",
3
- "version": "8.0.1",
3
+ "version": "8.0.3",
4
4
  "type": "module",
5
5
  "description": "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.",
6
6
  "homepage": "https://github.com/alexfernandez/loadtest",