blaze-performance-tester 3.1.52 → 3.1.55
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 +135 -17
- package/dist/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -151,6 +151,18 @@ var mathUtils = {
|
|
|
151
151
|
return sorted[Math.max(0, index)];
|
|
152
152
|
}
|
|
153
153
|
};
|
|
154
|
+
function getLoadMultiplier(elapsedMs, rampUpMs, steadyMs, rampDownMs) {
|
|
155
|
+
const totalDurationMs = rampUpMs + steadyMs + rampDownMs;
|
|
156
|
+
if (elapsedMs < rampUpMs && rampUpMs > 0) {
|
|
157
|
+
return elapsedMs / rampUpMs;
|
|
158
|
+
} else if (elapsedMs < rampUpMs + steadyMs) {
|
|
159
|
+
return 1;
|
|
160
|
+
} else if (elapsedMs < totalDurationMs && rampDownMs > 0) {
|
|
161
|
+
const timeIntoRampDown = elapsedMs - (rampUpMs + steadyMs);
|
|
162
|
+
return 1 - timeIntoRampDown / rampDownMs;
|
|
163
|
+
}
|
|
164
|
+
return elapsedMs >= totalDurationMs ? 0 : 1;
|
|
165
|
+
}
|
|
154
166
|
async function generateGeminiRCA(history, finalSummary, errorLogs) {
|
|
155
167
|
const apiKey = process.env.GEMINI_API_KEY;
|
|
156
168
|
if (!apiKey) {
|
|
@@ -272,6 +284,8 @@ function parseArguments(args) {
|
|
|
272
284
|
const clusterLogs = !args.includes("--no-cluster-logs");
|
|
273
285
|
let threads = 10;
|
|
274
286
|
let durationSec = 10;
|
|
287
|
+
let rampUpSec = 0;
|
|
288
|
+
let rampDownSec = 0;
|
|
275
289
|
let targetApdex = 0.85;
|
|
276
290
|
let targetErrorRate = 0.01;
|
|
277
291
|
let maxThreads = 300;
|
|
@@ -281,6 +295,10 @@ function parseArguments(args) {
|
|
|
281
295
|
if (threadsIdx !== -1) threads = parseInt(args[threadsIdx + 1], 10);
|
|
282
296
|
const durationIdx = args.indexOf("--duration");
|
|
283
297
|
if (durationIdx !== -1) durationSec = parseInt(args[durationIdx + 1], 10);
|
|
298
|
+
const rampUpIdx = args.indexOf("--ramp-up");
|
|
299
|
+
if (rampUpIdx !== -1) rampUpSec = parseInt(args[rampUpIdx + 1], 10);
|
|
300
|
+
const rampDownIdx = args.indexOf("--ramp-down");
|
|
301
|
+
if (rampDownIdx !== -1) rampDownSec = parseInt(args[rampDownIdx + 1], 10);
|
|
284
302
|
const apdexIdx = args.indexOf("--target-apdex");
|
|
285
303
|
if (apdexIdx !== -1) targetApdex = parseFloat(args[apdexIdx + 1]);
|
|
286
304
|
const errorIdx = args.indexOf("--target-error");
|
|
@@ -315,6 +333,8 @@ function parseArguments(args) {
|
|
|
315
333
|
clusterLogs,
|
|
316
334
|
threads,
|
|
317
335
|
durationSec,
|
|
336
|
+
rampUpSec,
|
|
337
|
+
rampDownSec,
|
|
318
338
|
targetApdex,
|
|
319
339
|
targetErrorRate,
|
|
320
340
|
maxThreads,
|
|
@@ -334,7 +354,7 @@ async function runBlazeCoreEngine(config) {
|
|
|
334
354
|
} catch (err) {
|
|
335
355
|
}
|
|
336
356
|
if (!rawRequests || rawRequests.length === 0) {
|
|
337
|
-
rawRequests = generateSimulationData(config.threads, config.durationSec, config.maxAllowedLatencyMs);
|
|
357
|
+
rawRequests = generateSimulationData(config.threads, config.durationSec, config.maxAllowedLatencyMs, config.rampUpSec, config.rampDownSec);
|
|
338
358
|
}
|
|
339
359
|
const warmUpCutoff = Math.floor(rawRequests.length * 0.1);
|
|
340
360
|
const steadyStateRequests = rawRequests.slice(warmUpCutoff);
|
|
@@ -492,7 +512,7 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
492
512
|
exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests, geminiAnalysisHtml);
|
|
493
513
|
}
|
|
494
514
|
async function runStandardStressTest(config) {
|
|
495
|
-
console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s]`);
|
|
515
|
+
console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s | RampUp: ${config.rampUpSec}s | RampDown: ${config.rampDownSec}s]`);
|
|
496
516
|
const { metrics, rawErrors, rawRequests } = await runBlazeCoreEngine(config);
|
|
497
517
|
printMatrixDashboard(metrics, config.threads);
|
|
498
518
|
const healthScore = computeHealthScoreValue(metrics.apdexScore, metrics.errorRate, metrics.p95LatencyMs, config.maxAllowedLatencyMs);
|
|
@@ -559,15 +579,23 @@ function getFallbackClusterLogs() {
|
|
|
559
579
|
add("HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 30000ms", 19);
|
|
560
580
|
return logs;
|
|
561
581
|
}
|
|
562
|
-
function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
|
|
582
|
+
function generateSimulationData(threads, durationSec, maxAllowedLatencyMs, rampUpSec = 0, rampDownSec = 0) {
|
|
563
583
|
const data = [];
|
|
564
584
|
const totalRequests = Math.max(15, threads * durationSec * 30);
|
|
565
585
|
const startTime = Date.now();
|
|
586
|
+
const rampUpMs = rampUpSec * 1e3;
|
|
587
|
+
const steadyMs = durationSec * 1e3;
|
|
588
|
+
const rampDownMs = rampDownSec * 1e3;
|
|
589
|
+
const totalTestMs = rampUpMs + steadyMs + rampDownMs;
|
|
566
590
|
const targetThresholdBreak = Math.max(5, Math.floor(maxAllowedLatencyMs * 0.05));
|
|
567
|
-
const isBreached = threads > targetThresholdBreak;
|
|
568
|
-
const stressFactor = isBreached ? Math.pow(threads / targetThresholdBreak, 1.5) : 1;
|
|
569
591
|
const errorPool = getFallbackClusterLogs();
|
|
570
592
|
for (let i = 0; i < totalRequests; i++) {
|
|
593
|
+
const offsetMs = Math.random() * totalTestMs;
|
|
594
|
+
const loadFactor = getLoadMultiplier(offsetMs, rampUpMs, steadyMs, rampDownMs);
|
|
595
|
+
if (loadFactor <= 0) continue;
|
|
596
|
+
const effectiveThreads = Math.max(1, threads * loadFactor);
|
|
597
|
+
const isBreached = effectiveThreads > targetThresholdBreak;
|
|
598
|
+
const stressFactor = isBreached ? Math.pow(effectiveThreads / targetThresholdBreak, 1.5) : 1;
|
|
571
599
|
const isError = Math.random() < (isBreached ? 0.05 : 1e-3);
|
|
572
600
|
const baseLatency = (25 + Math.random() * 35) * stressFactor;
|
|
573
601
|
let errorMessage;
|
|
@@ -579,7 +607,7 @@ function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
|
|
|
579
607
|
}
|
|
580
608
|
data.push({
|
|
581
609
|
durationMs: isError ? baseLatency * 0.1 : baseLatency,
|
|
582
|
-
timestampMs: startTime +
|
|
610
|
+
timestampMs: startTime + offsetMs,
|
|
583
611
|
dnsTimeMs: 1 + Math.random() * 0.9,
|
|
584
612
|
tcpTimeMs: 4 + Math.random() * 1.5,
|
|
585
613
|
tlsTimeMs: 6 + Math.random() * 1.8,
|
|
@@ -807,6 +835,30 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
807
835
|
</div>
|
|
808
836
|
</div>
|
|
809
837
|
</div>`;
|
|
838
|
+
const nlpQueryEngineHtml = `
|
|
839
|
+
<div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #0ea5e9; border-left: 4px solid #0ea5e9;">
|
|
840
|
+
<h3 style="color: #0ea5e9; display: flex; align-items: center; gap: 0.5rem; font-size: 1.2rem; margin-bottom: 0.5rem; border-bottom: none; padding-bottom: 0;">
|
|
841
|
+
\u{1F916} Natural Language Telemetry Insights Query Engine
|
|
842
|
+
</h3>
|
|
843
|
+
<p style="color: #94a3b8; font-size: 0.9rem; margin: 0 0 1rem 0;">
|
|
844
|
+
Ask questions of your load test results using natural phrasing to isolate and filter metric rows dynamically.
|
|
845
|
+
</p>
|
|
846
|
+
<div style="display: flex; gap: 0.75rem; margin-bottom: 0.75rem;">
|
|
847
|
+
<input type="text" id="nlpQueryInput" placeholder="e.g., show me tiers where latency > 40ms or error rate > 0%"
|
|
848
|
+
style="flex: 1; background: #090d16; border: 1px solid #1e293b; border-radius: 6px; padding: 0.75rem 1rem; color: #ffffff; font-size: 0.95rem; outline: none; transition: border-color 0.2s;">
|
|
849
|
+
<button id="nlpQueryBtn" style="background: #0ea5e9; color: #ffffff; border: none; border-radius: 6px; padding: 0.75rem 1.5rem; font-weight: 600; cursor: pointer; transition: background 0.2s;">
|
|
850
|
+
Query Matrix
|
|
851
|
+
</button>
|
|
852
|
+
</div>
|
|
853
|
+
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center;">
|
|
854
|
+
<span style="font-size: 0.8rem; color: #64748b; font-weight: bold; text-transform: uppercase; letter-spacing: 0.05em;">Suggestions:</span>
|
|
855
|
+
<button onclick="setNlpQuery('Show P95 latency > 50ms')" style="background: #1e293b; color: #cbd5e1; border: 1px solid #334155; border-radius: 4px; padding: 0.25rem 0.5rem; font-size: 0.8rem; cursor: pointer;">P95 Latency > 50ms</button>
|
|
856
|
+
<button onclick="setNlpQuery('Find tiers with errors > 0%')" style="background: #1e293b; color: #cbd5e1; border: 1px solid #334155; border-radius: 4px; padding: 0.25rem 0.5rem; font-size: 0.8rem; cursor: pointer;">Errors > 0%</button>
|
|
857
|
+
<button onclick="setNlpQuery('High concurrency > 25 VUs')" style="background: #1e293b; color: #cbd5e1; border: 1px solid #334155; border-radius: 4px; padding: 0.25rem 0.5rem; font-size: 0.8rem; cursor: pointer;">Concurrency > 25 VUs</button>
|
|
858
|
+
<button onclick="setNlpQuery('')" style="background: #1c1917; color: #f43f5e; border: 1px solid #44403c; border-radius: 4px; padding: 0.25rem 0.5rem; font-size: 0.8rem; cursor: pointer;">Reset Filter</button>
|
|
859
|
+
</div>
|
|
860
|
+
<div id="nlpQueryFeedback" style="margin-top: 0.75rem; font-size: 0.85rem; color: #38bdf8;"></div>
|
|
861
|
+
</div>`;
|
|
810
862
|
const breakdownRates = responseMatrix.breakdownRates || {};
|
|
811
863
|
const breakdownGridHtml = [400, 401, 403, 404, 429, 500, 502, 503, 504].map((code) => {
|
|
812
864
|
const rate = breakdownRates[code] || 0;
|
|
@@ -879,7 +931,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
879
931
|
<div class="container">
|
|
880
932
|
<div class="header">
|
|
881
933
|
<h1>\u{1F525} Blaze Core Performance Analysis</h1>
|
|
882
|
-
<div class="meta">Target Script: <code>${config.targetScript}</code
|
|
934
|
+
<div class="meta">Target Script: <code>${config.targetScript}</code> | Ramp-Up: ${config.rampUpSec}s | Ramp-Down: ${config.rampDownSec}s</div>
|
|
883
935
|
</div>
|
|
884
936
|
|
|
885
937
|
<div class="cards-wrapper">
|
|
@@ -1013,8 +1065,10 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1013
1065
|
</div>
|
|
1014
1066
|
</div>
|
|
1015
1067
|
</div>
|
|
1068
|
+
|
|
1069
|
+
${nlpQueryEngineHtml}
|
|
1016
1070
|
|
|
1017
|
-
<div class="card">
|
|
1071
|
+
<div class="card" style="margin-top: 2rem;">
|
|
1018
1072
|
<h3>Telemetry Matrix Logs</h3>
|
|
1019
1073
|
<table>
|
|
1020
1074
|
<thead>
|
|
@@ -1027,7 +1081,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1027
1081
|
<th>Status</th>
|
|
1028
1082
|
</tr>
|
|
1029
1083
|
</thead>
|
|
1030
|
-
<tbody>
|
|
1084
|
+
<tbody id="telemetryTableBody">
|
|
1031
1085
|
${history.map((h) => {
|
|
1032
1086
|
const isPassed = h.apdex >= config.targetApdex && h.errorRate <= config.targetErrorRate;
|
|
1033
1087
|
return `<tr>
|
|
@@ -1049,6 +1103,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1049
1103
|
</div>
|
|
1050
1104
|
|
|
1051
1105
|
<script>
|
|
1106
|
+
const telemetryHistory = ${JSON.stringify(history)};
|
|
1052
1107
|
const labels = ${JSON.stringify(labels)};
|
|
1053
1108
|
const baseThroughput = ${cards.throughput};
|
|
1054
1109
|
const baseLatency = ${cards.ttfb};
|
|
@@ -1058,12 +1113,75 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1058
1113
|
const simThroughput = document.getElementById('simThroughput');
|
|
1059
1114
|
const simLatency = document.getElementById('simLatency');
|
|
1060
1115
|
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1116
|
+
if (slider) {
|
|
1117
|
+
slider.addEventListener('input', (e) => {
|
|
1118
|
+
const val = parseInt(e.target.value, 10);
|
|
1119
|
+
sliderVal.textContent = val;
|
|
1120
|
+
const factor = val / ${config.threads || 10};
|
|
1121
|
+
simThroughput.textContent = (baseThroughput * Math.min(factor, 2.5)).toFixed(0) + '/s';
|
|
1122
|
+
simLatency.textContent = (baseLatency * Math.pow(factor, 0.8)).toFixed(1) + 'ms';
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
function executeNlpQuery(queryString) {
|
|
1127
|
+
const q = queryString.toLowerCase().trim();
|
|
1128
|
+
const rows = document.querySelectorAll('#telemetryTableBody tr');
|
|
1129
|
+
const feedback = document.getElementById('nlpQueryFeedback');
|
|
1130
|
+
|
|
1131
|
+
let filterFn = (row, data) => true;
|
|
1132
|
+
const numMatch = q.match(/(d+(?:.d+)?)/);
|
|
1133
|
+
const targetNum = numMatch ? parseFloat(numMatch[1]) : null;
|
|
1134
|
+
|
|
1135
|
+
const isGreater = q.includes('>') || q.includes('greater') || q.includes('above') || q.includes('more');
|
|
1136
|
+
const isLess = q.includes('<') || q.includes('less') || q.includes('below') || q.includes('under');
|
|
1137
|
+
|
|
1138
|
+
if (q.includes('latency') || q.includes('p95') || q.includes('ms')) {
|
|
1139
|
+
if (targetNum !== null) {
|
|
1140
|
+
filterFn = (row, data) => isLess ? (data.p95Latency < targetNum) : (data.p95Latency > targetNum);
|
|
1141
|
+
} else {
|
|
1142
|
+
filterFn = (row, data) => data.p95Latency > 0;
|
|
1143
|
+
}
|
|
1144
|
+
} else if (q.includes('error') || q.includes('fail') || q.includes('%')) {
|
|
1145
|
+
if (targetNum !== null) {
|
|
1146
|
+
const searchVal = q.includes('%') ? targetNum / 100 : targetNum;
|
|
1147
|
+
filterFn = (row, data) => isLess ? (data.errorRate < searchVal) : (data.errorRate > searchVal);
|
|
1148
|
+
} else {
|
|
1149
|
+
filterFn = (row, data) => data.errorRate > 0;
|
|
1150
|
+
}
|
|
1151
|
+
} else if (q.includes('concurrency') || q.includes('thread') || q.includes('vu')) {
|
|
1152
|
+
if (targetNum !== null) {
|
|
1153
|
+
filterFn = (row, data) => isLess ? (data.threads < targetNum) : (data.threads > targetNum);
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
let matchCount = 0;
|
|
1158
|
+
rows.forEach((row, idx) => {
|
|
1159
|
+
const data = telemetryHistory[idx];
|
|
1160
|
+
if (!data) return;
|
|
1161
|
+
if (filterFn(row, data)) {
|
|
1162
|
+
row.style.display = '';
|
|
1163
|
+
row.style.background = q !== '' ? 'rgba(14, 165, 233, 0.15)' : '';
|
|
1164
|
+
matchCount++;
|
|
1165
|
+
} else {
|
|
1166
|
+
row.style.display = 'none';
|
|
1167
|
+
}
|
|
1168
|
+
});
|
|
1169
|
+
|
|
1170
|
+
feedback.innerHTML = q === '' ? '' : '\u{1F50D} Match verification resolved <strong>' + matchCount + '</strong> parameters inside telemetry array.';
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
function setNlpQuery(str) {
|
|
1174
|
+
const input = document.getElementById('nlpQueryInput');
|
|
1175
|
+
input.value = str;
|
|
1176
|
+
executeNlpQuery(str);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
document.getElementById('nlpQueryBtn').addEventListener('click', () => {
|
|
1180
|
+
executeNlpQuery(document.getElementById('nlpQueryInput').value);
|
|
1181
|
+
});
|
|
1182
|
+
|
|
1183
|
+
document.getElementById('nlpQueryInput').addEventListener('keyup', (e) => {
|
|
1184
|
+
if (e.key === 'Enter') executeNlpQuery(e.target.value);
|
|
1067
1185
|
});
|
|
1068
1186
|
|
|
1069
1187
|
new Chart(document.getElementById('volumeChart'), {
|
|
@@ -1131,7 +1249,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1131
1249
|
const threadStep = ${config.stepSize || 15};
|
|
1132
1250
|
|
|
1133
1251
|
for (let i = 1; i <= 3; i++) {
|
|
1134
|
-
extendedLabels.push(
|
|
1252
|
+
extendedLabels.push('+' + (i * 5) + 's (AI Forecast)');
|
|
1135
1253
|
extendedThreads.push(lastThreads + (threadStep * i));
|
|
1136
1254
|
extendedTtf.push(null);
|
|
1137
1255
|
aiForecastData.push(Number((slope * (historicalCount + i - 1) + intercept).toFixed(1)));
|
|
@@ -1247,6 +1365,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1247
1365
|
function printUsage() {
|
|
1248
1366
|
console.log(`Blaze Core Performance Test CLI Engine
|
|
1249
1367
|
Options:
|
|
1250
|
-
--threads <count> | --duration <seconds> | --agentic | --cluster-logs | --correlate | --seo`);
|
|
1368
|
+
--threads <count> | --duration <seconds> | --ramp-up <seconds> | --ramp-down <seconds> | --agentic | --cluster-logs | --correlate | --seo`);
|
|
1251
1369
|
}
|
|
1252
1370
|
main().catch(console.error);
|
|
Binary file
|