|
1 | 1 | import http from 'http'; |
2 | 2 | import zlib from 'zlib'; |
3 | 3 |
|
| 4 | +// Configurable number of requests (default to 100) |
| 5 | +const numRequests = process.argv[2] ? parseInt(process.argv[2], 10) : 100; |
| 6 | + |
4 | 7 | const requestData = JSON.stringify({ |
5 | 8 | stmt: 'SELECT * FROM sys.summits', |
6 | 9 | }); |
7 | 10 |
|
8 | | -// Compress request data using gzip |
9 | | -zlib.deflate(requestData, (err, compressedData) => { |
10 | | - if (err) { |
11 | | - console.error('Compression failed:', err); |
12 | | - return; |
13 | | - } |
| 11 | +// Pre-compress the request data using deflate once (since it's the same for every request) |
| 12 | +const compressedData = zlib.deflateSync(requestData); |
| 13 | + |
| 14 | +/** |
| 15 | + * Formats a number of bytes into a human-readable string. |
| 16 | + * @param {number} bytes - The number of bytes. |
| 17 | + * @returns {string} The formatted string. |
| 18 | + */ |
| 19 | +function formatBytes(bytes) { |
| 20 | + if (bytes < 1024) return bytes + ' B'; |
| 21 | + const kb = bytes / 1024; |
| 22 | + if (kb < 1024) return kb.toFixed(2) + ' KB'; |
| 23 | + const mb = kb / 1024; |
| 24 | + if (mb < 1024) return mb.toFixed(2) + ' MB'; |
| 25 | + const gb = mb / 1024; |
| 26 | + return gb.toFixed(2) + ' GB'; |
| 27 | +} |
14 | 28 |
|
15 | | - const options = { |
16 | | - hostname: 'localhost', |
17 | | - port: 4200, |
18 | | - path: '/_sql', |
19 | | - method: 'POST', |
20 | | - headers: { |
21 | | - 'Content-Type': 'application/json', |
22 | | - 'Content-Encoding': 'deflate', // Tell CrateDB the request is compressed |
23 | | - Accept: 'application/json', |
24 | | - 'Accept-Encoding': 'gzip', |
25 | | - }, |
26 | | - }; |
27 | | - |
28 | | - const req = http.request(options, (res) => { |
29 | | - let responseData = ''; |
30 | | - |
31 | | - res.on('data', (chunk) => { |
32 | | - responseData += chunk; |
| 29 | +/** |
| 30 | + * Performs a single HTTP request using the given Accept-Encoding header. |
| 31 | + * @param {string|undefined} acceptEncoding - The Accept-Encoding header value (or undefined for no header). |
| 32 | + * @returns {Promise<number>} A promise that resolves with the response size in bytes. |
| 33 | + */ |
| 34 | +function performRequest(acceptEncoding) { |
| 35 | + return new Promise((resolve, reject) => { |
| 36 | + const options = { |
| 37 | + hostname: 'localhost', |
| 38 | + port: 4200, |
| 39 | + path: '/_sql', |
| 40 | + method: 'POST', |
| 41 | + headers: { |
| 42 | + 'Content-Type': 'application/json', |
| 43 | + 'Content-Encoding': 'deflate', // The request body is compressed with deflate |
| 44 | + Accept: 'application/json', |
| 45 | + }, |
| 46 | + }; |
| 47 | + |
| 48 | + if (acceptEncoding) { |
| 49 | + options.headers['Accept-Encoding'] = acceptEncoding; |
| 50 | + } |
| 51 | + |
| 52 | + const req = http.request(options, (res) => { |
| 53 | + let responseSize = 0; |
| 54 | + res.on('data', (chunk) => { |
| 55 | + responseSize += chunk.length; |
| 56 | + }); |
| 57 | + res.on('end', () => { |
| 58 | + resolve(responseSize); |
| 59 | + }); |
33 | 60 | }); |
34 | 61 |
|
35 | | - res.on('end', () => { |
36 | | - console.log('Response:', responseData); |
| 62 | + req.on('error', (error) => { |
| 63 | + reject(error); |
37 | 64 | }); |
38 | | - }); |
39 | 65 |
|
40 | | - req.on('error', (error) => { |
41 | | - console.error('Request error:', error); |
| 66 | + req.write(compressedData); |
| 67 | + req.end(); |
42 | 68 | }); |
| 69 | +} |
| 70 | + |
| 71 | +/** |
| 72 | + * Runs a batch of requests. |
| 73 | + * @param {string|undefined} acceptEncoding - The Accept-Encoding header value for the batch. |
| 74 | + * @param {number} count - Number of requests to perform. |
| 75 | + * @returns {Promise<{elapsedTime: number, totalResponseSize: number}>} |
| 76 | + */ |
| 77 | +async function runBatch(acceptEncoding, count) { |
| 78 | + const startTime = Date.now(); |
| 79 | + let totalResponseSize = 0; |
| 80 | + for (let i = 0; i < count; i++) { |
| 81 | + try { |
| 82 | + const size = await performRequest(acceptEncoding); |
| 83 | + totalResponseSize += size; |
| 84 | + } catch (error) { |
| 85 | + console.error('Request error:', error); |
| 86 | + } |
| 87 | + } |
| 88 | + const elapsedTime = Date.now() - startTime; |
| 89 | + return { elapsedTime, totalResponseSize }; |
| 90 | +} |
| 91 | + |
| 92 | +/** |
| 93 | + * Runs all test batches and logs the aggregated metrics. |
| 94 | + */ |
| 95 | +async function runTests() { |
| 96 | + console.log(`Running ${numRequests} requests with Accept-Encoding: gzip`); |
| 97 | + const gzipResults = await runBatch('gzip', numRequests); |
| 98 | + console.log( |
| 99 | + `GZIP - Total time: ${gzipResults.elapsedTime} ms, ` + |
| 100 | + `Average time: ${(gzipResults.elapsedTime / numRequests).toFixed(2)} ms, ` + |
| 101 | + `Total response size: ${formatBytes(gzipResults.totalResponseSize)}` |
| 102 | + ); |
| 103 | + |
| 104 | + console.log(`Running ${numRequests} requests with Accept-Encoding: deflate`); |
| 105 | + const deflateResults = await runBatch('deflate', numRequests); |
| 106 | + console.log( |
| 107 | + `DEFLATE - Total time: ${deflateResults.elapsedTime} ms, ` + |
| 108 | + `Average time: ${(deflateResults.elapsedTime / numRequests).toFixed(2)} ms, ` + |
| 109 | + `Total response size: ${formatBytes(deflateResults.totalResponseSize)}` |
| 110 | + ); |
| 111 | + |
| 112 | + console.log(`Running ${numRequests} requests without Accept-Encoding header`); |
| 113 | + const noHeaderResults = await runBatch(undefined, numRequests); |
| 114 | + console.log( |
| 115 | + `No Accept-Encoding - Total time: ${noHeaderResults.elapsedTime} ms, ` + |
| 116 | + `Average time: ${(noHeaderResults.elapsedTime / numRequests).toFixed(2)} ms, ` + |
| 117 | + `Total response size: ${formatBytes(noHeaderResults.totalResponseSize)}` |
| 118 | + ); |
| 119 | +} |
43 | 120 |
|
44 | | - req.write(compressedData); // Send gzipped request body |
45 | | - req.end(); |
| 121 | +runTests().catch((error) => { |
| 122 | + console.error('Error during tests:', error); |
46 | 123 | }); |
0 commit comments