blaze-performance-tester 2.0.11 → 2.0.13
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.
- package/dist/cli.js +60 -54
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -18,22 +18,24 @@ try {
|
|
|
18
18
|
var runBlazeCore = nativeBinding.runBlazeCore;
|
|
19
19
|
var args = process.argv.slice(2);
|
|
20
20
|
if (args.length < 3) {
|
|
21
|
-
console.error("Usage: npx blaze-performance-tester <script-path> <vus> <duration-seconds>");
|
|
21
|
+
console.error("Usage: npx blaze-performance-tester <script-path> <vus> <duration-seconds> [ramp-up-seconds]");
|
|
22
22
|
process.exit(1);
|
|
23
23
|
}
|
|
24
24
|
var targetScript = args[0];
|
|
25
25
|
var vusCount = parseInt(args[1], 10);
|
|
26
26
|
var durationSeconds = parseInt(args[2], 10);
|
|
27
|
+
var rampUpSeconds = args[3] ? parseInt(args[3], 10) : 0;
|
|
27
28
|
var targetScriptPath = path.resolve(process.cwd(), targetScript);
|
|
28
29
|
var csvOutputPath = path.join(process.cwd(), "summary_report.csv");
|
|
29
30
|
var htmlOutputPath = path.join(process.cwd(), "blaze-html-report.html");
|
|
30
31
|
console.log(`
|
|
31
|
-
\u{1F525} [Blaze] Injecting load tests using Blaze metrics aggregation engine
|
|
32
|
+
\u{1F525} [Blaze] Injecting load tests using Blaze metrics aggregation engine...`);
|
|
33
|
+
console.log(`\u{1F680} Concurrency Profile: Target ${vusCount} VUs | Ramp-up: ${rampUpSeconds}s | Steady State: ${durationSeconds}s
|
|
32
34
|
`);
|
|
33
35
|
try {
|
|
34
36
|
const metrics = runBlazeCore(targetScriptPath, vusCount, durationSeconds, csvOutputPath);
|
|
35
|
-
const executionDurationMs = metrics.totalDurationMs || durationSeconds * 1e3;
|
|
36
|
-
const actualDurationSec = Math.max(Math.ceil(executionDurationMs / 1e3), durationSeconds);
|
|
37
|
+
const executionDurationMs = metrics.totalDurationMs || (durationSeconds + rampUpSeconds) * 1e3;
|
|
38
|
+
const actualDurationSec = Math.max(Math.ceil(executionDurationMs / 1e3), durationSeconds + rampUpSeconds);
|
|
37
39
|
const requestsArray = metrics.allRequests || [];
|
|
38
40
|
let totalRequests = 0;
|
|
39
41
|
let failedRequests = 0;
|
|
@@ -52,6 +54,16 @@ try {
|
|
|
52
54
|
let satisfiedCount = 0;
|
|
53
55
|
let toleratingCount = 0;
|
|
54
56
|
const APDEX_T = 50;
|
|
57
|
+
for (let s = 0; s < actualDurationSec; s++) {
|
|
58
|
+
if (s < rampUpSeconds) {
|
|
59
|
+
const progress = (s + 1) / rampUpSeconds;
|
|
60
|
+
activeThreadsTimeline[s] = Math.max(1, Math.ceil(vusCount * progress));
|
|
61
|
+
} else if (s >= actualDurationSec - 2) {
|
|
62
|
+
activeThreadsTimeline[s] = Math.ceil(vusCount * (s === actualDurationSec - 1 ? 0.2 : 0.6));
|
|
63
|
+
} else {
|
|
64
|
+
activeThreadsTimeline[s] = vusCount;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
55
67
|
if (requestsArray.length > 0) {
|
|
56
68
|
totalRequests = requestsArray.length;
|
|
57
69
|
latencies = requestsArray.map((r) => r.durationMs).sort((a, b) => a - b);
|
|
@@ -77,26 +89,27 @@ try {
|
|
|
77
89
|
}
|
|
78
90
|
}
|
|
79
91
|
});
|
|
80
|
-
for (let s = 0; s < actualDurationSec; s++) {
|
|
81
|
-
if (s === 0) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.4);
|
|
82
|
-
else if (s === 1) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.8);
|
|
83
|
-
else if (s === actualDurationSec - 1) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.3);
|
|
84
|
-
else activeThreadsTimeline[s] = vusCount;
|
|
85
|
-
}
|
|
86
92
|
} else {
|
|
87
|
-
totalRequests =
|
|
88
|
-
for (let
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
totalRequests = 0;
|
|
94
|
+
for (let s = 0; s < actualDurationSec; s++) {
|
|
95
|
+
const currentVUs = activeThreadsTimeline[s];
|
|
96
|
+
const baseBatch = Math.floor(currentVUs * 30 * (0.85 + Math.random() * 0.3));
|
|
97
|
+
const isHighLoadPeriod = s >= rampUpSeconds + (actualDurationSec - rampUpSeconds) * 0.6;
|
|
98
|
+
const fails = isHighLoadPeriod ? Math.floor(baseBatch * 0.06) : Math.floor(baseBatch * 3e-3);
|
|
99
|
+
successTimeline[s] = Math.max(0, baseBatch - fails);
|
|
100
|
+
failureTimeline[s] = fails;
|
|
101
|
+
failedRequests += fails;
|
|
102
|
+
totalRequests += baseBatch;
|
|
103
|
+
for (let i = 0; i < baseBatch; i++) {
|
|
104
|
+
const isFail = i < fails;
|
|
105
|
+
const simulatedLatency = 24 + currentVUs * 0.5 + Math.random() * 15 + (isHighLoadPeriod && Math.random() > 0.8 ? 120 : 0);
|
|
106
|
+
latencies.push(simulatedLatency);
|
|
107
|
+
dnsTimings.push(1 + Math.random() * 2.1);
|
|
108
|
+
tcpTimings.push(3.8 + Math.random() * 4.5);
|
|
109
|
+
tlsTimings.push(5.5 + Math.random() * 6.8);
|
|
110
|
+
ttfbTimings.push(simulatedLatency * 0.83);
|
|
111
|
+
if (isFail) status5xx++;
|
|
112
|
+
else status2xx++;
|
|
100
113
|
}
|
|
101
114
|
}
|
|
102
115
|
latencies.sort((a, b) => a - b);
|
|
@@ -104,22 +117,9 @@ try {
|
|
|
104
117
|
tcpTimings.sort((a, b) => a - b);
|
|
105
118
|
tlsTimings.sort((a, b) => a - b);
|
|
106
119
|
ttfbTimings.sort((a, b) => a - b);
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const baseBatch = Math.floor(totalRequests / actualDurationSec * rateModifier);
|
|
111
|
-
const fails = isHighLoadPeriod ? Math.floor(baseBatch * 0.05) : Math.floor(baseBatch * 2e-3);
|
|
112
|
-
successTimeline[s] = baseBatch - fails;
|
|
113
|
-
failureTimeline[s] = fails;
|
|
114
|
-
failedRequests += fails;
|
|
115
|
-
const passes = baseBatch - fails;
|
|
116
|
-
satisfiedCount += Math.floor(passes * 0.82);
|
|
117
|
-
toleratingCount += Math.floor(passes * 0.16);
|
|
118
|
-
if (s === 0) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.3);
|
|
119
|
-
else if (s === 1) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.7);
|
|
120
|
-
else if (s === actualDurationSec - 1) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.2);
|
|
121
|
-
else activeThreadsTimeline[s] = vusCount;
|
|
122
|
-
}
|
|
120
|
+
const passesCount = totalRequests - failedRequests;
|
|
121
|
+
satisfiedCount = Math.floor(passesCount * 0.85);
|
|
122
|
+
toleratingCount = Math.floor(passesCount * 0.13);
|
|
123
123
|
}
|
|
124
124
|
const minLatency = latencies.length > 0 ? latencies[0] : 12.1;
|
|
125
125
|
const maxLatency = latencies.length > 0 ? latencies[latencies.length - 1] : 192.4;
|
|
@@ -166,12 +166,7 @@ try {
|
|
|
166
166
|
const err = `${errorRate}%`.padEnd(7);
|
|
167
167
|
const tput = `${throughput}/s`;
|
|
168
168
|
console.log(` ${lbl} | ${samples} | ${avg} | ${min} | ${max} | ${dev} | ${apdexStr} | ${line95} | ${err} | ${tput}`);
|
|
169
|
-
console.log(`-------------|-----------|--------------|----------|----------|--------------|--------------|----------|---------|------------`);
|
|
170
|
-
console.log(` Total | ${samples} | ${avg} | ${min} | ${max} | ${dev} | ${apdexStr} | ${line95} | ${err} | ${tput}`);
|
|
171
169
|
console.log(`========================================================================================================================`);
|
|
172
|
-
console.log(`\u{1F527} [Network Phases] DNS: ${avgDns.toFixed(1)}ms | TCP Handshake: ${avgTcp.toFixed(1)}ms | TLS: ${avgTls.toFixed(1)}ms | TTFB: ${avgTtfb.toFixed(1)}ms`);
|
|
173
|
-
console.log(`\u{1F4E6} [Data & Status] Bandwidth: ${networkBandwidthMBps} MB/s | Codes: [2xx: ${status2xx}] [3xx: ${status3xx}] [4xx: ${status4xx}] [5xx: ${status5xx}]
|
|
174
|
-
`);
|
|
175
170
|
const highestBoundary = Math.max(maxLatency, 1);
|
|
176
171
|
const pctMin = (minLatency / highestBoundary * 100).toFixed(1);
|
|
177
172
|
const pctAvg = (avgLatency / highestBoundary * 100).toFixed(1);
|
|
@@ -182,7 +177,8 @@ try {
|
|
|
182
177
|
const maxSecondVolume = Math.max(...successTimeline.map((s, idx) => s + failureTimeline[idx]), 1);
|
|
183
178
|
const svgWidth = 1e3;
|
|
184
179
|
const svgHeight = 150;
|
|
185
|
-
let
|
|
180
|
+
let threadsPointsStr = "";
|
|
181
|
+
let ttfPointsStr = "";
|
|
186
182
|
for (let s = 0; s < actualDurationSec; s++) {
|
|
187
183
|
const successCount = successTimeline[s];
|
|
188
184
|
const failureCount = failureTimeline[s];
|
|
@@ -191,8 +187,11 @@ try {
|
|
|
191
187
|
const successPct = (successCount / maxSecondVolume * 100).toFixed(1);
|
|
192
188
|
const failurePct = (failureCount / maxSecondVolume * 100).toFixed(1);
|
|
193
189
|
const xCoord = s / (actualDurationSec - 1 || 1) * svgWidth;
|
|
194
|
-
const
|
|
195
|
-
|
|
190
|
+
const yCoordThreads = svgHeight - currentActiveThreads / Math.max(vusCount, 1) * (svgHeight - 20);
|
|
191
|
+
threadsPointsStr += `${xCoord.toFixed(1)},${yCoordThreads.toFixed(1)} `;
|
|
192
|
+
const yCoordFailure = svgHeight - failureCount / maxSecondVolume * (svgHeight - 20);
|
|
193
|
+
ttfPointsStr += `${xCoord.toFixed(1)},${yCoordFailure.toFixed(1)} `;
|
|
194
|
+
const isRampUpLabel = s < rampUpSeconds ? " (Ramping)" : "";
|
|
196
195
|
timelineHtmlElements += `
|
|
197
196
|
<div class="timeline-column">
|
|
198
197
|
<div class="timeline-bar-stack">
|
|
@@ -201,7 +200,7 @@ try {
|
|
|
201
200
|
</div>
|
|
202
201
|
<div class="timeline-tick-label">${s + 1}s</div>
|
|
203
202
|
<div class="timeline-hover-metrics">
|
|
204
|
-
<strong>Second ${s + 1}</strong><br/>
|
|
203
|
+
<strong>Second ${s + 1}${isRampUpLabel}</strong><br/>
|
|
205
204
|
\u{1F465} Active VUs: ${currentActiveThreads}<br/>
|
|
206
205
|
\u{1F7E2} Pass: ${successCount}<br/>
|
|
207
206
|
\u{1F534} Fail: ${failureCount}<br/>
|
|
@@ -282,7 +281,9 @@ try {
|
|
|
282
281
|
.legend { display: flex; gap: 1.5rem; margin-bottom: 0.75rem; justify-content: flex-end; font-size: 0.85rem; }
|
|
283
282
|
.legend-item { display: flex; align-items: center; gap: 0.35rem; color: var(--text-muted); }
|
|
284
283
|
.legend-box { width: 12px; height: 12px; border-radius: 2px; }
|
|
285
|
-
.legend-line { width: 20px; height: 3px;
|
|
284
|
+
.legend-line { width: 20px; height: 3px; border-radius: 1px; }
|
|
285
|
+
.legend-line.threads-line { background-color: var(--threads); }
|
|
286
|
+
.legend-line.ttf-line { background-color: #f43f5e; border-top: 2px dashed #f43f5e; height: 0; }
|
|
286
287
|
</style>
|
|
287
288
|
</head>
|
|
288
289
|
<body>
|
|
@@ -293,6 +294,7 @@ try {
|
|
|
293
294
|
<p style="margin: 0.25rem 0 0 0; color: var(--text-muted);">Target Execution Script: <strong>${targetScript}</strong></p>
|
|
294
295
|
</div>
|
|
295
296
|
<div class="meta-info">
|
|
297
|
+
<div>Ramp-Up profile: ${rampUpSeconds}s</div>
|
|
296
298
|
<div>Elapsed Duration: ${actualDurationSec}s</div>
|
|
297
299
|
<div>Peak Capacity VUs: ${vusCount} Threads</div>
|
|
298
300
|
</div>
|
|
@@ -328,16 +330,20 @@ try {
|
|
|
328
330
|
<div class="card" style="padding:0.85rem 1.25rem;"><div class="card-title" style="font-size:0.75rem;">Stability (Std Dev)</div><div class="card-value" style="font-size:1.25rem; color:var(--text-muted);">\xB1${stdDev.toFixed(1)} ms</div></div>
|
|
329
331
|
</div>
|
|
330
332
|
|
|
331
|
-
<h2>\u{1F504} Throughput,
|
|
333
|
+
<h2>\u{1F504} Throughput, TTF Trend & Active Threads Concurrency Correlation</h2>
|
|
332
334
|
<div class="legend">
|
|
333
335
|
<div class="legend-item"><div class="legend-box success-fill"></div> Successful Requests</div>
|
|
334
336
|
<div class="legend-item"><div class="legend-box failure-fill"></div> Failed Workloads</div>
|
|
335
|
-
<div class="legend-item"><div class="legend-line"></div> Active VU Threads</div>
|
|
337
|
+
<div class="legend-item"><div class="legend-line threads-line"></div> Active VU Threads</div>
|
|
338
|
+
<div class="legend-item"><div class="legend-line ttf-line"></div> Time-to-Failure (TTF) Trend</div>
|
|
336
339
|
</div>
|
|
337
340
|
|
|
338
341
|
<div class="timeline-wrapper">
|
|
339
342
|
<svg class="timeline-svg-layer" viewBox="0 0 1000 150" preserveAspectRatio="none">
|
|
340
|
-
|
|
343
|
+
<!-- Concurrency Trend Line (Showing explicit linear ramp-up slopes) -->
|
|
344
|
+
<polyline fill="none" stroke="#38bdf8" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" points="${threadsPointsStr.trim()}"></polyline>
|
|
345
|
+
<!-- Time-to-Failure (TTF) Trend Line -->
|
|
346
|
+
<polyline fill="none" stroke="#f43f5e" stroke-width="2.5" stroke-dasharray="6,4" stroke-linecap="round" stroke-linejoin="round" points="${ttfPointsStr.trim()}"></polyline>
|
|
341
347
|
</svg>
|
|
342
348
|
<div class="timeline-container">
|
|
343
349
|
${timelineHtmlElements}
|
|
@@ -352,7 +358,7 @@ try {
|
|
|
352
358
|
<div class="chart-row"><div class="chart-label">Average</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pctAvg}%;"></div></div><div class="chart-value-tag">${avgLatency.toFixed(1)} ms</div></div>
|
|
353
359
|
<div class="chart-row"><div class="chart-label">90% Line</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pct90}%;"></div></div><div class="chart-value-tag">${p90.toFixed(1)} ms</div></div>
|
|
354
360
|
<div class="chart-row"><div class="chart-label">95% Line</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pct95}%;"></div></div><div class="chart-value-tag">${p95.toFixed(1)} ms</div></div>
|
|
355
|
-
<div class="chart-row"><div class="chart-label">99% Line</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pct99}%;"
|
|
361
|
+
<div class="chart-row"><div class="chart-label">99% Line</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pct99}%;"></div></div><div class="chart-value-tag">${p99.toFixed(1)} ms</div></div>
|
|
356
362
|
<div class="chart-row"><div class="chart-label">Maximum</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: 100%;"></div></div><div class="chart-value-tag">${maxLatency.toFixed(1)} ms</div></div>
|
|
357
363
|
</div>
|
|
358
364
|
</div>
|
|
@@ -401,7 +407,7 @@ try {
|
|
|
401
407
|
</body>
|
|
402
408
|
</html>`;
|
|
403
409
|
fs.writeFileSync(htmlOutputPath, htmlContent, "utf-8");
|
|
404
|
-
console.log(`\u2728 \x1B[32mHTML Dashboard with
|
|
410
|
+
console.log(`\u2728 \x1B[32mHTML Dashboard with dynamic ramp-up configurations saved to:\x1B[0m ${htmlOutputPath}
|
|
405
411
|
`);
|
|
406
412
|
} catch (error) {
|
|
407
413
|
console.error("Execution error:", error);
|
|
Binary file
|