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 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 = vusCount * durationSeconds * 30;
88
- for (let i = 0; i < totalRequests; i++) {
89
- const isFail = Math.random() > 0.97;
90
- const simulatedLatency = 28 + Math.random() * 18 + (Math.random() > 0.95 ? Math.random() * 140 : 0);
91
- latencies.push(simulatedLatency);
92
- dnsTimings.push(1 + Math.random() * 2.5);
93
- tcpTimings.push(4 + Math.random() * 5.2);
94
- tlsTimings.push(6.1 + Math.random() * 7.5);
95
- ttfbTimings.push(simulatedLatency * 0.82);
96
- if (isFail) {
97
- status5xx++;
98
- } else {
99
- status2xx++;
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
- for (let s = 0; s < actualDurationSec; s++) {
108
- const isHighLoadPeriod = s >= Math.floor(actualDurationSec * 0.6);
109
- const rateModifier = isHighLoadPeriod ? 1.25 : 0.95;
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 pointsStr = "";
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 yCoord = svgHeight - currentActiveThreads / Math.max(vusCount, 1) * (svgHeight - 20);
195
- pointsStr += `${xCoord.toFixed(1)},${yCoord.toFixed(1)} `;
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; background-color: var(--threads); border-radius: 1px; }
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, Errors & Active Threads Concurrency Correlation</h2>
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
- <polyline fill="none" stroke="#38bdf8" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" points="${pointsStr.trim()}"></polyline>
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}%;">%;"></div></div><div class="chart-value-tag">${p99.toFixed(1)} ms</div></div>
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 multi-layered protocol tracking saved to:\x1B[0m ${htmlOutputPath}
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blaze-performance-tester",
3
- "version": "2.0.11",
3
+ "version": "2.0.13",
4
4
  "description": "A high-performance, multi-threaded load testing engine built with Rust and QuickJS.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",