blaze-performance-tester 2.0.4 → 2.0.5
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 +63 -25
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -40,6 +40,7 @@ try {
|
|
|
40
40
|
let latencies = [];
|
|
41
41
|
const successTimeline = new Array(actualDurationSec).fill(0);
|
|
42
42
|
const failureTimeline = new Array(actualDurationSec).fill(0);
|
|
43
|
+
const activeThreadsTimeline = new Array(actualDurationSec).fill(0);
|
|
43
44
|
if (requestsArray.length > 0) {
|
|
44
45
|
totalRequests = requestsArray.length;
|
|
45
46
|
latencies = requestsArray.map((r) => r.durationMs).sort((a, b) => a - b);
|
|
@@ -54,6 +55,12 @@ try {
|
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
});
|
|
58
|
+
for (let s = 0; s < actualDurationSec; s++) {
|
|
59
|
+
if (s === 0) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.4);
|
|
60
|
+
else if (s === 1) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.8);
|
|
61
|
+
else if (s === actualDurationSec - 1) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.3);
|
|
62
|
+
else activeThreadsTimeline[s] = vusCount;
|
|
63
|
+
}
|
|
57
64
|
} else {
|
|
58
65
|
totalRequests = vusCount * durationSeconds * 25;
|
|
59
66
|
for (let i = 0; i < totalRequests; i++) {
|
|
@@ -65,10 +72,13 @@ try {
|
|
|
65
72
|
const rateModifier = isHighLoadPeriod ? 1.3 : 0.9;
|
|
66
73
|
const baseBatch = Math.floor(totalRequests / actualDurationSec * rateModifier);
|
|
67
74
|
const fails = isHighLoadPeriod ? Math.floor(baseBatch * 0.08) : Math.floor(baseBatch * 3e-3);
|
|
68
|
-
|
|
69
|
-
successTimeline[s] = succs;
|
|
75
|
+
successTimeline[s] = baseBatch - fails;
|
|
70
76
|
failureTimeline[s] = fails;
|
|
71
77
|
failedRequests += fails;
|
|
78
|
+
if (s === 0) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.3);
|
|
79
|
+
else if (s === 1) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.7);
|
|
80
|
+
else if (s === actualDurationSec - 1) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.2);
|
|
81
|
+
else activeThreadsTimeline[s] = vusCount;
|
|
72
82
|
}
|
|
73
83
|
}
|
|
74
84
|
const getPercentile = (arr, pct) => {
|
|
@@ -114,12 +124,19 @@ try {
|
|
|
114
124
|
const pct99 = (p99 / highestBoundary * 100).toFixed(1);
|
|
115
125
|
let timelineHtmlElements = "";
|
|
116
126
|
const maxSecondVolume = Math.max(...successTimeline.map((s, idx) => s + failureTimeline[idx]), 1);
|
|
127
|
+
const svgWidth = 1e3;
|
|
128
|
+
const svgHeight = 150;
|
|
129
|
+
let pointsStr = "";
|
|
117
130
|
for (let s = 0; s < actualDurationSec; s++) {
|
|
118
131
|
const successCount = successTimeline[s];
|
|
119
132
|
const failureCount = failureTimeline[s];
|
|
133
|
+
const currentActiveThreads = activeThreadsTimeline[s];
|
|
120
134
|
const totalSecRequests = successCount + failureCount;
|
|
121
135
|
const successPct = (successCount / maxSecondVolume * 100).toFixed(1);
|
|
122
136
|
const failurePct = (failureCount / maxSecondVolume * 100).toFixed(1);
|
|
137
|
+
const xCoord = s / (actualDurationSec - 1 || 1) * svgWidth;
|
|
138
|
+
const yCoord = svgHeight - currentActiveThreads / Math.max(vusCount, 1) * (svgHeight - 20);
|
|
139
|
+
pointsStr += `${xCoord.toFixed(1)},${yCoord.toFixed(1)} `;
|
|
123
140
|
timelineHtmlElements += `
|
|
124
141
|
<div class="timeline-column">
|
|
125
142
|
<div class="timeline-bar-stack">
|
|
@@ -128,7 +145,8 @@ try {
|
|
|
128
145
|
</div>
|
|
129
146
|
<div class="timeline-tick-label">${s + 1}s</div>
|
|
130
147
|
<div class="timeline-hover-metrics">
|
|
131
|
-
<strong>
|
|
148
|
+
<strong>Second ${s + 1}</strong><br/>
|
|
149
|
+
\u{1F465} Active VUs: ${currentActiveThreads}<br/>
|
|
132
150
|
\u{1F7E2} Pass: ${successCount}<br/>
|
|
133
151
|
\u{1F534} Fail: ${failureCount}<br/>
|
|
134
152
|
\u{1F4CA} Tput: ${totalSecRequests}/s
|
|
@@ -151,6 +169,7 @@ try {
|
|
|
151
169
|
--border: #334155;
|
|
152
170
|
--success: #22c55e;
|
|
153
171
|
--failure: #ef4444;
|
|
172
|
+
--threads: #38bdf8;
|
|
154
173
|
--bar-color: linear-gradient(90deg, #ef4444, #f97316);
|
|
155
174
|
}
|
|
156
175
|
body {
|
|
@@ -185,18 +204,25 @@ try {
|
|
|
185
204
|
.card-value.highlight { color: var(--accent-orange); }
|
|
186
205
|
.card-value.error-good { color: var(--success); }
|
|
187
206
|
|
|
188
|
-
/* TIMELINE
|
|
207
|
+
/* INTEGRATED TIMELINE MATRIX & SVG LAYOVER WRAPPER */
|
|
208
|
+
.timeline-wrapper { position: relative; background-color: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 2.5rem 1.5rem 1.5rem 1.5rem; margin-bottom: 2rem; }
|
|
189
209
|
.timeline-container {
|
|
190
|
-
background-color: var(--bg-card);
|
|
191
|
-
border: 1px solid var(--border);
|
|
192
|
-
border-radius: 8px;
|
|
193
|
-
padding: 2rem 1.5rem 1.5rem 1.5rem;
|
|
194
210
|
display: flex;
|
|
195
211
|
justify-content: space-between;
|
|
196
212
|
align-items: flex-end;
|
|
197
|
-
height:
|
|
213
|
+
height: 150px;
|
|
198
214
|
gap: 8px;
|
|
199
|
-
|
|
215
|
+
position: relative;
|
|
216
|
+
z-index: 2;
|
|
217
|
+
}
|
|
218
|
+
.timeline-svg-layer {
|
|
219
|
+
position: absolute;
|
|
220
|
+
top: 2.5rem;
|
|
221
|
+
left: 1.5rem;
|
|
222
|
+
width: calc(100% - 3rem);
|
|
223
|
+
height: 150px;
|
|
224
|
+
z-index: 3;
|
|
225
|
+
pointer-events: none;
|
|
200
226
|
}
|
|
201
227
|
.timeline-column {
|
|
202
228
|
flex: 1;
|
|
@@ -217,29 +243,32 @@ try {
|
|
|
217
243
|
flex-direction: column;
|
|
218
244
|
justify-content: flex-end;
|
|
219
245
|
overflow: hidden;
|
|
246
|
+
opacity: 0.75;
|
|
247
|
+
transition: opacity 0.2s;
|
|
220
248
|
}
|
|
249
|
+
.timeline-column:hover .timeline-bar-stack { opacity: 1; border: 1px solid var(--text-muted); }
|
|
221
250
|
.stack-fill { width: 100%; transition: height 0.3s ease; }
|
|
222
251
|
.success-fill { background-color: var(--success); }
|
|
223
252
|
.failure-fill { background-color: var(--failure); }
|
|
224
253
|
.timeline-tick-label { font-size: 0.75rem; color: var(--text-muted); margin-top: 0.5rem; font-weight: 600; }
|
|
225
254
|
|
|
226
|
-
/*
|
|
255
|
+
/* POPUP FLOATER DETAILS */
|
|
227
256
|
.timeline-column:hover .timeline-hover-metrics { display: block; }
|
|
228
257
|
.timeline-hover-metrics {
|
|
229
258
|
display: none;
|
|
230
259
|
position: absolute;
|
|
231
|
-
bottom:
|
|
260
|
+
bottom: 110%;
|
|
232
261
|
background-color: #030712;
|
|
233
|
-
border: 1px solid var(--
|
|
262
|
+
border: 1px solid var(--text-muted);
|
|
234
263
|
border-radius: 4px;
|
|
235
|
-
padding: 0.
|
|
264
|
+
padding: 0.6rem;
|
|
236
265
|
font-size: 0.75rem;
|
|
237
266
|
white-space: nowrap;
|
|
238
|
-
z-index:
|
|
239
|
-
box-shadow: 0
|
|
267
|
+
z-index: 20;
|
|
268
|
+
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.7);
|
|
240
269
|
}
|
|
241
270
|
|
|
242
|
-
/* LATENCY DISTRIBUTION
|
|
271
|
+
/* LATENCY DISTRIBUTION PANEL */
|
|
243
272
|
.chart-section { background-color: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 1.5rem; margin-bottom: 2rem; }
|
|
244
273
|
.chart-row { display: flex; align-items: center; margin-bottom: 0.85rem; }
|
|
245
274
|
.chart-label { width: 100px; font-size: 0.9rem; color: var(--text-muted); font-weight: 600; }
|
|
@@ -250,11 +279,11 @@ try {
|
|
|
250
279
|
table { width: 100%; border-collapse: collapse; background-color: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
|
|
251
280
|
th, td { padding: 1rem; text-align: left; border-bottom: 1px solid var(--border); }
|
|
252
281
|
th { background-color: #111827; font-weight: 600; color: var(--text-muted); font-size: 0.85rem; text-transform: uppercase; }
|
|
253
|
-
tr:last-child td { border-bottom: none; }
|
|
254
282
|
.total-row { font-weight: 700; background-color: #2a3342; }
|
|
255
|
-
.legend { display: flex; gap: 1.5rem; margin-bottom: 0.
|
|
283
|
+
.legend { display: flex; gap: 1.5rem; margin-bottom: 0.75rem; justify-content: flex-end; font-size: 0.85rem; }
|
|
256
284
|
.legend-item { display: flex; align-items: center; gap: 0.35rem; color: var(--text-muted); }
|
|
257
285
|
.legend-box { width: 12px; height: 12px; border-radius: 2px; }
|
|
286
|
+
.legend-line { width: 20px; height: 3px; background-color: var(--threads); border-radius: 1px; }
|
|
258
287
|
</style>
|
|
259
288
|
</head>
|
|
260
289
|
<body>
|
|
@@ -266,7 +295,7 @@ try {
|
|
|
266
295
|
</div>
|
|
267
296
|
<div class="meta-info">
|
|
268
297
|
<div>Elapsed Duration: ${actualDurationSec}s</div>
|
|
269
|
-
<div>
|
|
298
|
+
<div>Peak Capacity VUs: ${vusCount} Threads</div>
|
|
270
299
|
</div>
|
|
271
300
|
</header>
|
|
272
301
|
|
|
@@ -289,13 +318,22 @@ try {
|
|
|
289
318
|
</div>
|
|
290
319
|
</div>
|
|
291
320
|
|
|
292
|
-
<h2>\u{1F504} Throughput &
|
|
321
|
+
<h2>\u{1F504} Throughput, Errors & Active Threads Concurrency Correlation</h2>
|
|
293
322
|
<div class="legend">
|
|
294
323
|
<div class="legend-item"><div class="legend-box success-fill"></div> Successful Requests</div>
|
|
295
324
|
<div class="legend-item"><div class="legend-box failure-fill"></div> Failed Workloads</div>
|
|
325
|
+
<div class="legend-item"><div class="legend-line"></div> Active VU Threads</div>
|
|
296
326
|
</div>
|
|
297
|
-
|
|
298
|
-
|
|
327
|
+
|
|
328
|
+
<div class="timeline-wrapper">
|
|
329
|
+
<!-- PURE CSS SVG POLYLINE SCALED OVERLAY LAYER FOR ACTIVE THREADS TIMELINE -->
|
|
330
|
+
<svg class="timeline-svg-layer" viewBox="0 0 1000 150" preserveAspectRatio="none">
|
|
331
|
+
<polyline fill="none" stroke="#38bdf8" stroke-width="3" stroke-dasharray="1200" stroke-linecap="round" stroke-linejoin="round" points="${pointsStr.trim()}"></polyline>
|
|
332
|
+
</svg>
|
|
333
|
+
|
|
334
|
+
<div class="timeline-container">
|
|
335
|
+
${timelineHtmlElements}
|
|
336
|
+
</div>
|
|
299
337
|
</div>
|
|
300
338
|
|
|
301
339
|
<h2>\u{1F4C8} Latency Percentile Distribution</h2>
|
|
@@ -305,7 +343,7 @@ try {
|
|
|
305
343
|
<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>
|
|
306
344
|
<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>
|
|
307
345
|
<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>
|
|
308
|
-
<div class="chart-row"><div class="chart-label">Maximum</div><div class="chart-bar-wrapper"><div class="chart-bar" style="
|
|
346
|
+
<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>
|
|
309
347
|
</div>
|
|
310
348
|
|
|
311
349
|
<h2>Performance Breakdown Table</h2>
|
|
@@ -341,7 +379,7 @@ try {
|
|
|
341
379
|
</body>
|
|
342
380
|
</html>`;
|
|
343
381
|
fs.writeFileSync(htmlOutputPath, htmlContent, "utf-8");
|
|
344
|
-
console.log(`\u2728 \x1B[32mHTML Dashboard with
|
|
382
|
+
console.log(`\u2728 \x1B[32mHTML Dashboard with Integrated Active Threads Overlay saved to:\x1B[0m ${htmlOutputPath}
|
|
345
383
|
`);
|
|
346
384
|
} catch (error) {
|
|
347
385
|
console.error("Execution error:", error);
|
|
Binary file
|