blaze-performance-tester 3.1.58 → 3.1.61
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 +286 -295
- package/dist/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env ts-node
|
|
2
2
|
|
|
3
3
|
// cli.ts
|
|
4
|
-
import { Command } from "commander";
|
|
5
|
-
import chalk from "chalk";
|
|
6
4
|
import { createRequire } from "module";
|
|
7
5
|
import * as path from "path";
|
|
8
6
|
import { fileURLToPath } from "url";
|
|
@@ -136,7 +134,8 @@ var blazeCore;
|
|
|
136
134
|
try {
|
|
137
135
|
blazeCore = require2(nativeBindingPath);
|
|
138
136
|
} catch (e) {
|
|
139
|
-
console.error(
|
|
137
|
+
console.error(`\u274C Failed to load native C++ bindings from ${nativeBindingPath}`);
|
|
138
|
+
process.exit(1);
|
|
140
139
|
}
|
|
141
140
|
var mathUtils = {
|
|
142
141
|
mean: (arr) => arr.length === 0 ? 0 : arr.reduce((p, c) => p + c, 0) / arr.length,
|
|
@@ -152,46 +151,6 @@ var mathUtils = {
|
|
|
152
151
|
return sorted[Math.max(0, index)];
|
|
153
152
|
}
|
|
154
153
|
};
|
|
155
|
-
function getMockRegionalData() {
|
|
156
|
-
return [
|
|
157
|
-
{ zone: "us-east-1", location: "N. Virginia", latencyAvg: 24, p99: 68, errorRate: 0.02, status: "optimal" },
|
|
158
|
-
{ zone: "eu-central-1", location: "Frankfurt", latencyAvg: 42, p99: 115, errorRate: 0.14, status: "stable" },
|
|
159
|
-
{ zone: "ap-south-1", location: "Mumbai", latencyAvg: 88, p99: 210, errorRate: 0.85, status: "elevated" },
|
|
160
|
-
{ zone: "ap-northeast-1", location: "Tokyo", latencyAvg: 55, p99: 130, errorRate: 0.05, status: "stable" },
|
|
161
|
-
{ zone: "sa-east-1", location: "S\xE3o Paulo", latencyAvg: 112, p99: 285, errorRate: 1.2, status: "critical" }
|
|
162
|
-
];
|
|
163
|
-
}
|
|
164
|
-
function renderStatusBadge(status) {
|
|
165
|
-
switch (status) {
|
|
166
|
-
case "optimal":
|
|
167
|
-
return chalk.green("\u{1F7E2} OPTIMAL");
|
|
168
|
-
case "stable":
|
|
169
|
-
return chalk.green("\u{1F7E2} STABLE");
|
|
170
|
-
case "elevated":
|
|
171
|
-
return chalk.yellow("\u{1F7E1} ELEVATED");
|
|
172
|
-
case "critical":
|
|
173
|
-
return chalk.red("\u{1F534} CRITICAL");
|
|
174
|
-
default:
|
|
175
|
-
return chalk.gray("\u26AA UNKNOWN");
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
function renderGeographicHeatmap(regions) {
|
|
179
|
-
console.log(chalk.bold.cyan("\n============================================================"));
|
|
180
|
-
console.log(chalk.bold.cyan(" BLAZE: GEOGRAPHIC LATENCY HEATMAP & MATRIX "));
|
|
181
|
-
console.log(chalk.bold.cyan("============================================================\n"));
|
|
182
|
-
console.log(
|
|
183
|
-
`${chalk.bold("Zone".padEnd(16))} | ${chalk.bold("Location".padEnd(14))} | ${chalk.bold("Avg (ms)".padEnd(10))} | ${chalk.bold("P99 (ms)".padEnd(10))} | ${chalk.bold("Error Rate".padEnd(12))} | ${chalk.bold("Status")}`
|
|
184
|
-
);
|
|
185
|
-
console.log("-".repeat(78));
|
|
186
|
-
for (const r of regions) {
|
|
187
|
-
const errorColor = r.errorRate > 1 ? chalk.red : r.errorRate > 0.5 ? chalk.yellow : chalk.green;
|
|
188
|
-
const latencyColor = r.latencyAvg > 100 ? chalk.yellow : chalk.white;
|
|
189
|
-
console.log(
|
|
190
|
-
`${r.zone.padEnd(16)} | ${r.location.padEnd(14)} | ${latencyColor(String(r.latencyAvg).padEnd(10))} | ${String(r.p99).padEnd(10)} | ${errorColor(`${r.errorRate.toFixed(2)}%`).padEnd(20)} | ${renderStatusBadge(r.status)}`
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
console.log("\n");
|
|
194
|
-
}
|
|
195
154
|
function getLoadMultiplier(elapsedMs, rampUpMs, steadyMs, rampDownMs) {
|
|
196
155
|
const totalDurationMs = rampUpMs + steadyMs + rampDownMs;
|
|
197
156
|
if (elapsedMs < rampUpMs && rampUpMs > 0) {
|
|
@@ -209,64 +168,20 @@ function loadBaselineData(filePath) {
|
|
|
209
168
|
try {
|
|
210
169
|
const resolved = path.resolve(filePath);
|
|
211
170
|
if (fs.existsSync(resolved)) {
|
|
212
|
-
console.log(
|
|
171
|
+
console.log(`\u{1F4C1} Loaded baseline comparison telemetry from: ${resolved}`);
|
|
213
172
|
return JSON.parse(fs.readFileSync(resolved, "utf-8"));
|
|
214
173
|
}
|
|
215
174
|
} catch (e) {
|
|
216
|
-
console.error(
|
|
175
|
+
console.error(`\u274C Failed to parse baseline telemetry file: ${e}`);
|
|
217
176
|
}
|
|
218
177
|
return null;
|
|
219
178
|
}
|
|
220
|
-
function calculateSeoImpactMetrics(avgLatencyMs) {
|
|
221
|
-
const baseLineOptimalMs = 200;
|
|
222
|
-
if (avgLatencyMs <= baseLineOptimalMs) {
|
|
223
|
-
return { trafficLoss: 0, status: "Excellent", crawlPenalty: "None", color: "#10b981" };
|
|
224
|
-
}
|
|
225
|
-
const delayFactor = avgLatencyMs - baseLineOptimalMs;
|
|
226
|
-
const trafficLoss = Math.min(94, Math.floor(delayFactor / 1200 * 100));
|
|
227
|
-
let status = "Healthy";
|
|
228
|
-
let crawlPenalty = "Minimal impact on indexing rates.";
|
|
229
|
-
let color = "#10b981";
|
|
230
|
-
if (trafficLoss >= 50) {
|
|
231
|
-
status = "Critical Exposure";
|
|
232
|
-
crawlPenalty = "Severe penalty. Googlebot will heavily restrict crawl budgets due to timeout risks.";
|
|
233
|
-
color = "#ef4444";
|
|
234
|
-
} else if (trafficLoss >= 20) {
|
|
235
|
-
status = "Moderate Danger";
|
|
236
|
-
crawlPenalty = "Delayed indexation. Core Web Vital thresholds are officially breached.";
|
|
237
|
-
color = "#f97316";
|
|
238
|
-
} else if (trafficLoss > 5) {
|
|
239
|
-
status = "Minor Impact";
|
|
240
|
-
crawlPenalty = "Slightly elevated bounce trends could impact highly volatile rankings.";
|
|
241
|
-
color = "#eab308";
|
|
242
|
-
}
|
|
243
|
-
return { trafficLoss, status, crawlPenalty, color };
|
|
244
|
-
}
|
|
245
|
-
function computeHealthScoreValue(apdex, errorRate, p95Latency, maxAllowedLatency) {
|
|
246
|
-
const apdexComponent = apdex * 50;
|
|
247
|
-
const errorComponent = Math.max(0, 1 - errorRate) * 30;
|
|
248
|
-
const latencyRatio = p95Latency / maxAllowedLatency;
|
|
249
|
-
const latencyComponent = Math.max(0, 1 - Math.min(1, latencyRatio)) * 20;
|
|
250
|
-
return Math.min(100, Math.max(0, Math.round(apdexComponent + errorComponent + latencyComponent)));
|
|
251
|
-
}
|
|
252
|
-
function getFallbackClusterLogs() {
|
|
253
|
-
const logs = [];
|
|
254
|
-
const add = (msg, count) => {
|
|
255
|
-
for (let i = 0; i < count; i++) logs.push(`[2026-07-08T12:08:18.000Z] ${msg}`);
|
|
256
|
-
};
|
|
257
|
-
add("JsonWebTokenError: jwt expired at JMT.verify (\\app\\node_modules\\jsonwebtoken\\index.js:5)", 37);
|
|
258
|
-
add("Error: ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1)", 33);
|
|
259
|
-
add("TypeError: Cannot read properties of undefined (reading 'config_id') at Object.execute (\\app\\dist\\handler.js:42:19)", 31);
|
|
260
|
-
add("HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 15000ms", 25);
|
|
261
|
-
add("HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 30000ms", 19);
|
|
262
|
-
return logs;
|
|
263
|
-
}
|
|
264
179
|
async function generateGeminiRCA(history, finalSummary, errorLogs) {
|
|
265
180
|
const apiKey = process.env.GEMINI_API_KEY;
|
|
266
181
|
if (!apiKey) {
|
|
267
182
|
return `<span style="color: #ef4444;">\u26A0\uFE0F Gemini API Key missing. Export GEMINI_API_KEY to enable automated 2.5 Flash RCA analysis.</span>`;
|
|
268
183
|
}
|
|
269
|
-
console.log(
|
|
184
|
+
console.log("\u{1F9E0} Querying Gemini 2.5 Flash for Advanced Root Cause Analysis...");
|
|
270
185
|
const formattedHistory = history.map(
|
|
271
186
|
(h) => `Threads: ${h.threads} VUs | Throughput: ${h.throughput.toFixed(1)} req/s | Avg Latency: ${h.avgLatency.toFixed(1)}ms | Errors: ${(h.errorRate * 100).toFixed(2)}% | Apdex: ${h.apdex.toFixed(2)}`
|
|
272
187
|
).join("\n");
|
|
@@ -322,97 +237,127 @@ CRITICAL: Return ONLY plain paragraph text. Do not use markdown headers (###). I
|
|
|
322
237
|
}
|
|
323
238
|
return candidateText.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>").replace(/\n/g, "<br>");
|
|
324
239
|
} catch (error) {
|
|
325
|
-
console.error(
|
|
240
|
+
console.error("\u274C Gemini API processing breakdown failure:", error);
|
|
326
241
|
return `<span style="color: #f87171;">Failed to generate AI diagnosis framework: ${error.message || error}</span>`;
|
|
327
242
|
}
|
|
328
243
|
}
|
|
329
|
-
function
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
let statusCode = 200;
|
|
350
|
-
if (isError) {
|
|
351
|
-
const targetResponseCodes = [400, 401, 403, 404, 429, 500, 502, 503, 504];
|
|
352
|
-
statusCode = targetResponseCodes[Math.floor(Math.random() * targetResponseCodes.length)];
|
|
353
|
-
errorMessage = errorPool[Math.floor(Math.random() * errorPool.length)];
|
|
244
|
+
async function executeTestPipeline(config) {
|
|
245
|
+
if (!blazeCore || typeof blazeCore.runCorrelatedTransaction !== "function") {
|
|
246
|
+
console.error("\u274C Error: runCorrelatedTransaction function is missing from the compiled binary bindings.");
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const transactionalScenario = [
|
|
250
|
+
{
|
|
251
|
+
name: "Fetch Target Anti-Forgery Nonce",
|
|
252
|
+
url: "https://httpbin.org/json",
|
|
253
|
+
method: "GET",
|
|
254
|
+
body: null
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
name: "Perform Contextual Post Action",
|
|
258
|
+
url: "https://httpbin.org/post?verification=${csrf_token}",
|
|
259
|
+
method: "POST",
|
|
260
|
+
body: JSON.stringify({
|
|
261
|
+
data: "performance_payload",
|
|
262
|
+
security_token: "${csrf_token}"
|
|
263
|
+
})
|
|
354
264
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
265
|
+
];
|
|
266
|
+
console.log(`Launching automated script transaction analysis for: ${config.targetScript}`);
|
|
267
|
+
const sanitizedSteps = transactionalScenario.map((step) => ({
|
|
268
|
+
...step,
|
|
269
|
+
body: typeof step.body === "string" ? step.body : step.body ? JSON.stringify(step.body) : ""
|
|
270
|
+
}));
|
|
271
|
+
const success = await blazeCore.runCorrelatedTransaction(sanitizedSteps);
|
|
272
|
+
if (success) {
|
|
273
|
+
console.log("Pipeline run complete. Parameter tracking successfully mitigated breaking changes.");
|
|
364
274
|
}
|
|
365
|
-
return data.sort((a, b) => a.timestampMs - b.timestampMs);
|
|
366
275
|
}
|
|
367
|
-
function
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
276
|
+
async function main() {
|
|
277
|
+
const args = process.argv.slice(2);
|
|
278
|
+
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
279
|
+
printUsage();
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const config = parseArguments(args);
|
|
283
|
+
if (config.isCorrelate) {
|
|
284
|
+
await executeTestPipeline(config);
|
|
285
|
+
}
|
|
286
|
+
if (config.isAgentic) {
|
|
287
|
+
await runIntelligentAgenticStressTest(config);
|
|
288
|
+
} else {
|
|
289
|
+
await runStandardStressTest(config);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function parseArguments(args) {
|
|
293
|
+
const targetScript = path.resolve(args[0] || ".");
|
|
294
|
+
const isAgentic = args.includes("--agentic");
|
|
295
|
+
const isCorrelate = args.includes("--correlate");
|
|
296
|
+
const isSeo = args.includes("--seo");
|
|
297
|
+
const clusterLogs = !args.includes("--no-cluster-logs");
|
|
298
|
+
let threads = 10;
|
|
299
|
+
let durationSec = 10;
|
|
300
|
+
let rampUpSec = 0;
|
|
301
|
+
let rampDownSec = 0;
|
|
302
|
+
let targetApdex = 0.85;
|
|
303
|
+
let targetErrorRate = 0.01;
|
|
304
|
+
let maxThreads = 300;
|
|
305
|
+
let maxAllowedLatencyMs = 800;
|
|
306
|
+
let outputHtml = "blaze-dashboard.html";
|
|
307
|
+
let baselinePath = null;
|
|
308
|
+
const threadsIdx = args.indexOf("--threads");
|
|
309
|
+
if (threadsIdx !== -1) threads = parseInt(args[threadsIdx + 1], 10);
|
|
310
|
+
const durationIdx = args.indexOf("--duration");
|
|
311
|
+
if (durationIdx !== -1) durationSec = parseInt(args[durationIdx + 1], 10);
|
|
312
|
+
const rampUpIdx = args.indexOf("--ramp-up");
|
|
313
|
+
if (rampUpIdx !== -1) rampUpSec = parseInt(args[rampUpIdx + 1], 10);
|
|
314
|
+
const rampDownIdx = args.indexOf("--ramp-down");
|
|
315
|
+
if (rampDownIdx !== -1) rampDownSec = parseInt(args[rampDownIdx + 1], 10);
|
|
316
|
+
const apdexIdx = args.indexOf("--target-apdex");
|
|
317
|
+
if (apdexIdx !== -1) targetApdex = parseFloat(apdexIdx + 1);
|
|
318
|
+
const errorIdx = args.indexOf("--target-error");
|
|
319
|
+
if (errorIdx !== -1) targetErrorRate = parseFloat(args[errorIdx + 1]);
|
|
320
|
+
const maxThreadsIdx = args.indexOf("--max-threads");
|
|
321
|
+
if (maxThreadsIdx !== -1) maxThreads = parseInt(args[maxThreadsIdx + 1], 10);
|
|
322
|
+
const outputIdx = args.indexOf("--output");
|
|
323
|
+
if (outputIdx !== -1) outputHtml = args[outputIdx + 1];
|
|
324
|
+
const baselineIdx = args.indexOf("--baseline");
|
|
325
|
+
if (baselineIdx !== -1) baselinePath = args[baselineIdx + 1];
|
|
326
|
+
const positionalNums = args.slice(1).filter((arg) => !arg.startsWith("-")).map(Number).filter((n) => !isNaN(n));
|
|
327
|
+
if (positionalNums.length > 0 && threadsIdx === -1 && !isAgentic) threads = positionalNums[0];
|
|
328
|
+
if (positionalNums.length > 1 && durationIdx === -1 && !isAgentic) durationSec = positionalNums[1];
|
|
329
|
+
if (positionalNums.length > 2 && errorIdx === -1 && !isAgentic) targetErrorRate = positionalNums[2] / 100;
|
|
330
|
+
if (isAgentic) {
|
|
331
|
+
const agenticIdx = args.indexOf("--agentic");
|
|
332
|
+
const trailingArgs = args.slice(agenticIdx + 1).filter((arg) => !arg.startsWith("--"));
|
|
333
|
+
if (trailingArgs[0]) maxThreads = parseInt(trailingArgs[0], 10);
|
|
334
|
+
if (trailingArgs[1]) {
|
|
335
|
+
let rawLatency = parseFloat(trailingArgs[1]);
|
|
336
|
+
if (rawLatency < 50) {
|
|
337
|
+
maxAllowedLatencyMs = rawLatency * 1e3;
|
|
338
|
+
} else {
|
|
339
|
+
maxAllowedLatencyMs = rawLatency;
|
|
340
|
+
}
|
|
388
341
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
const errorRate = totalRequests === 0 ? 0 : errorCount / totalRequests;
|
|
392
|
-
const T = 50;
|
|
393
|
-
const satisfied = requests.filter((r) => r.durationMs <= T && r.statusCode < 400).length;
|
|
394
|
-
const tolerating = requests.filter((r) => r.durationMs > T && r.durationMs <= T * 4 && r.statusCode < 400).length;
|
|
395
|
-
const apdexScore = totalRequests === 0 ? 0 : (satisfied + tolerating / 2) / totalRequests;
|
|
396
|
-
const bandwidthMb = totalRequests * 1.2 / durationSec / 10;
|
|
342
|
+
if (trailingArgs[2]) targetErrorRate = parseFloat(trailingArgs[2]) / 100;
|
|
343
|
+
}
|
|
397
344
|
return {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
bandwidthMb,
|
|
415
|
-
codeCounts
|
|
345
|
+
targetScript,
|
|
346
|
+
isAgentic,
|
|
347
|
+
isCorrelate,
|
|
348
|
+
isSeo,
|
|
349
|
+
clusterLogs,
|
|
350
|
+
threads,
|
|
351
|
+
durationSec,
|
|
352
|
+
rampUpSec,
|
|
353
|
+
rampDownSec,
|
|
354
|
+
targetApdex,
|
|
355
|
+
targetErrorRate,
|
|
356
|
+
maxThreads,
|
|
357
|
+
stepSize: 15,
|
|
358
|
+
maxAllowedLatencyMs,
|
|
359
|
+
outputHtml,
|
|
360
|
+
baselinePath
|
|
416
361
|
};
|
|
417
362
|
}
|
|
418
363
|
async function runBlazeCoreEngine(config) {
|
|
@@ -436,62 +381,40 @@ async function runBlazeCoreEngine(config) {
|
|
|
436
381
|
}, 1e3);
|
|
437
382
|
});
|
|
438
383
|
}
|
|
439
|
-
function
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
console.log(`-----------------------------------------------------------------------------------------`);
|
|
444
|
-
console.log(`| ${pad(threads, 7)} | ${pad(m.totalRequests, 9)} | ${pad(m.avgLatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.p95LatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.requestsPerSecond.toFixed(0) + "/s", 10)} | ${pad((m.errorRate * 100).toFixed(2) + "%", 10)} | ${pad(m.apdexScore.toFixed(2), 9)} |`);
|
|
445
|
-
console.log(`-----------------------------------------------------------------------------------------`);
|
|
446
|
-
}
|
|
447
|
-
function generateFinalAgentReport(history, score) {
|
|
448
|
-
if (history.length === 0) return;
|
|
449
|
-
const peakThroughput = Math.max(...history.map((h) => h.throughput));
|
|
450
|
-
const cleanRuns = history.filter((h) => h.apdex >= 0.85 && h.errorRate <= 0.01);
|
|
451
|
-
const maxSafeThreads = cleanRuns.length > 0 ? cleanRuns[cleanRuns.length - 1].threads : history[0].threads;
|
|
452
|
-
console.log(chalk.bold.green("\n======================================================="));
|
|
453
|
-
console.log(chalk.bold.cyan("\u{1F9E0} INTELLIGENT AUTONOMOUS LOAD AGENT FINAL REPORT"));
|
|
454
|
-
console.log(chalk.bold.green("======================================================="));
|
|
455
|
-
console.log(`\u{1F7E2} Unified Blaze Health Score : ${score}/100`);
|
|
456
|
-
console.log(`\u{1F3C6} Recommended Max Safe Concurrency : ${maxSafeThreads} allocation threads`);
|
|
457
|
-
console.log(`\u{1F4A5} Infrastructure Failure Boundary : ${history[history.length - 1].threads} concurrency allocation`);
|
|
458
|
-
console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
|
|
459
|
-
console.log(chalk.bold.green("=======================================================\n"));
|
|
460
|
-
}
|
|
461
|
-
async function executeTestPipeline(config) {
|
|
462
|
-
if (!blazeCore || typeof blazeCore.runCorrelatedTransaction !== "function") {
|
|
463
|
-
console.error(chalk.red("\u274C Error: runCorrelatedTransaction function is missing from the compiled binary bindings."));
|
|
464
|
-
return;
|
|
384
|
+
function calculateSeoImpactMetrics(avgLatencyMs) {
|
|
385
|
+
const baseLineOptimalMs = 200;
|
|
386
|
+
if (avgLatencyMs <= baseLineOptimalMs) {
|
|
387
|
+
return { trafficLoss: 0, status: "Excellent", crawlPenalty: "None", color: "#10b981" };
|
|
465
388
|
}
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
console.log(chalk.blue(`Launching automated script transaction analysis for: ${config.targetScript}`));
|
|
484
|
-
const sanitizedSteps = transactionalScenario.map((step) => ({
|
|
485
|
-
...step,
|
|
486
|
-
body: typeof step.body === "string" ? step.body : step.body ? JSON.stringify(step.body) : ""
|
|
487
|
-
}));
|
|
488
|
-
const success = await blazeCore.runCorrelatedTransaction(sanitizedSteps);
|
|
489
|
-
if (success) {
|
|
490
|
-
console.log(chalk.green("Pipeline run complete. Parameter tracking successfully mitigated breaking changes."));
|
|
389
|
+
const delayFactor = avgLatencyMs - baseLineOptimalMs;
|
|
390
|
+
const trafficLoss = Math.min(94, Math.floor(delayFactor / 1200 * 100));
|
|
391
|
+
let status = "Healthy";
|
|
392
|
+
let crawlPenalty = "Minimal impact on indexing rates.";
|
|
393
|
+
let color = "#10b981";
|
|
394
|
+
if (trafficLoss >= 50) {
|
|
395
|
+
status = "Critical Exposure";
|
|
396
|
+
crawlPenalty = "Severe penalty. Googlebot will heavily restrict crawl budgets due to timeout risks.";
|
|
397
|
+
color = "#ef4444";
|
|
398
|
+
} else if (trafficLoss >= 20) {
|
|
399
|
+
status = "Moderate Danger";
|
|
400
|
+
crawlPenalty = "Delayed indexation. Core Web Vital thresholds are officially breached.";
|
|
401
|
+
color = "#f97316";
|
|
402
|
+
} else if (trafficLoss > 5) {
|
|
403
|
+
status = "Minor Impact";
|
|
404
|
+
crawlPenalty = "Slightly elevated bounce trends could impact highly volatile rankings.";
|
|
405
|
+
color = "#eab308";
|
|
491
406
|
}
|
|
407
|
+
return { trafficLoss, status, crawlPenalty, color };
|
|
408
|
+
}
|
|
409
|
+
function computeHealthScoreValue(apdex, errorRate, p95Latency, maxAllowedLatency) {
|
|
410
|
+
const apdexComponent = apdex * 50;
|
|
411
|
+
const errorComponent = Math.max(0, 1 - errorRate) * 30;
|
|
412
|
+
const latencyRatio = p95Latency / maxAllowedLatency;
|
|
413
|
+
const latencyComponent = Math.max(0, 1 - Math.min(1, latencyRatio)) * 20;
|
|
414
|
+
return Math.min(100, Math.max(0, Math.round(apdexComponent + errorComponent + latencyComponent)));
|
|
492
415
|
}
|
|
493
416
|
async function runIntelligentAgenticStressTest(config) {
|
|
494
|
-
console.log(
|
|
417
|
+
console.log("\u{1F680} Initializing Intelligent Autonomous Load Agent...");
|
|
495
418
|
const history = [];
|
|
496
419
|
let aggregateErrorLogs = [];
|
|
497
420
|
let lastRawRequests = [];
|
|
@@ -505,7 +428,7 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
505
428
|
let verdictReason = "The system successfully scaled and remained stable within parameters up to maximum configured capacity allocations.";
|
|
506
429
|
let saturationKneePoint = null;
|
|
507
430
|
while (currentThreads <= config.maxThreads && isStable) {
|
|
508
|
-
console.log(
|
|
431
|
+
console.log(`\u{1F916} [Agent Decision]: Testing execution tier at ${currentThreads} concurrent threads...`);
|
|
509
432
|
const { metrics, rawErrors, rawRequests } = await runBlazeCoreEngine({ ...config, threads: currentThreads });
|
|
510
433
|
if (config.clusterLogs) {
|
|
511
434
|
aggregateErrorLogs = aggregateErrorLogs.concat(rawErrors);
|
|
@@ -598,7 +521,7 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
598
521
|
if (config.isSeo) {
|
|
599
522
|
const finalLat = finalState.avgLatency || finalSummaryCards.ttfb;
|
|
600
523
|
const seo = calculateSeoImpactMetrics(finalLat);
|
|
601
|
-
console.log(
|
|
524
|
+
console.log(`\u{1F50E} [SEO Impact Report]: Target latency under stress (${finalLat.toFixed(1)}ms) triggers a predicted Search Visibility Loss of -${seo.trafficLoss}% [Status: ${seo.status}].`);
|
|
602
525
|
}
|
|
603
526
|
const breakdownRates = {};
|
|
604
527
|
Object.keys(globalCodeCounts).forEach((codeStr) => {
|
|
@@ -609,11 +532,11 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
609
532
|
exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests, geminiAnalysisHtml, baselineData);
|
|
610
533
|
}
|
|
611
534
|
async function runStandardStressTest(config) {
|
|
612
|
-
console.log(
|
|
535
|
+
console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s | RampUp: ${config.rampUpSec}s | RampDown: ${config.rampDownSec}s]`);
|
|
613
536
|
const { metrics, rawErrors, rawRequests } = await runBlazeCoreEngine(config);
|
|
614
537
|
printMatrixDashboard(metrics, config.threads);
|
|
615
538
|
const healthScore = computeHealthScoreValue(metrics.apdexScore, metrics.errorRate, metrics.p95LatencyMs, config.maxAllowedLatencyMs);
|
|
616
|
-
console.log(
|
|
539
|
+
console.log(`\u{1F49A} Unified Blaze Health Score: ${healthScore}/100`);
|
|
617
540
|
const history = [{
|
|
618
541
|
threads: config.threads,
|
|
619
542
|
avgLatency: metrics.avgLatencyMs,
|
|
@@ -650,7 +573,7 @@ async function runStandardStressTest(config) {
|
|
|
650
573
|
const geminiAnalysisHtml = await generateGeminiRCA(history, finalSummaryCards, rawErrors.length > 0 ? rawErrors : getFallbackClusterLogs());
|
|
651
574
|
if (config.isSeo) {
|
|
652
575
|
const seo = calculateSeoImpactMetrics(metrics.avgLatencyMs);
|
|
653
|
-
console.log(
|
|
576
|
+
console.log(`\u{1F50E} [SEO Impact Report]: Target latency (${metrics.avgLatencyMs.toFixed(1)}ms) triggers a predicted Search Visibility Loss of -${seo.trafficLoss}% [Status: ${seo.status}].`);
|
|
654
577
|
}
|
|
655
578
|
const breakdownRates = {};
|
|
656
579
|
Object.keys(metrics.codeCounts).forEach((codeStr) => {
|
|
@@ -667,6 +590,129 @@ async function runStandardStressTest(config) {
|
|
|
667
590
|
breakdownRates
|
|
668
591
|
}, finalSummaryCards, rawRequests, geminiAnalysisHtml, baselineData);
|
|
669
592
|
}
|
|
593
|
+
function getFallbackClusterLogs() {
|
|
594
|
+
const logs = [];
|
|
595
|
+
const add = (msg, count) => {
|
|
596
|
+
for (let i = 0; i < count; i++) logs.push(`[2026-07-08T12:08:18.000Z] ${msg}`);
|
|
597
|
+
};
|
|
598
|
+
add("JsonWebTokenError: jwt expired at JMT.verify (\\app\\node_modules\\jsonwebtoken\\index.js:5)", 37);
|
|
599
|
+
add("Error: ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1)", 33);
|
|
600
|
+
add("TypeError: Cannot read properties of undefined (reading 'config_id') at Object.execute (\\app\\dist\\handler.js:42:19)", 31);
|
|
601
|
+
add("HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 15000ms", 25);
|
|
602
|
+
add("HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 30000ms", 19);
|
|
603
|
+
return logs;
|
|
604
|
+
}
|
|
605
|
+
function generateSimulationData(threads, durationSec, maxAllowedLatencyMs, rampUpSec = 0, rampDownSec = 0) {
|
|
606
|
+
const data = [];
|
|
607
|
+
const totalRequests = Math.max(15, threads * durationSec * 30);
|
|
608
|
+
const startTime = Date.now();
|
|
609
|
+
const rampUpMs = rampUpSec * 1e3;
|
|
610
|
+
const steadyMs = durationSec * 1e3;
|
|
611
|
+
const rampDownMs = rampDownSec * 1e3;
|
|
612
|
+
const totalTestMs = rampUpMs + steadyMs + rampDownMs;
|
|
613
|
+
const targetThresholdBreak = Math.max(5, Math.floor(maxAllowedLatencyMs * 0.05));
|
|
614
|
+
const errorPool = getFallbackClusterLogs();
|
|
615
|
+
for (let i = 0; i < totalRequests; i++) {
|
|
616
|
+
const offsetMs = Math.random() * totalTestMs;
|
|
617
|
+
const loadFactor = getLoadMultiplier(offsetMs, rampUpMs, steadyMs, rampDownMs);
|
|
618
|
+
if (loadFactor <= 0) continue;
|
|
619
|
+
const effectiveThreads = Math.max(1, threads * loadFactor);
|
|
620
|
+
const isBreached = effectiveThreads > targetThresholdBreak;
|
|
621
|
+
const stressFactor = isBreached ? Math.pow(effectiveThreads / targetThresholdBreak, 1.5) : 1;
|
|
622
|
+
const isError = Math.random() < (isBreached ? 0.05 : 1e-3);
|
|
623
|
+
const baseLatency = (25 + Math.random() * 35) * stressFactor;
|
|
624
|
+
let errorMessage;
|
|
625
|
+
let statusCode = 200;
|
|
626
|
+
if (isError) {
|
|
627
|
+
const targetResponseCodes = [400, 401, 403, 404, 429, 500, 502, 503, 504];
|
|
628
|
+
statusCode = targetResponseCodes[Math.floor(Math.random() * targetResponseCodes.length)];
|
|
629
|
+
errorMessage = errorPool[Math.floor(Math.random() * errorPool.length)];
|
|
630
|
+
}
|
|
631
|
+
data.push({
|
|
632
|
+
durationMs: isError ? baseLatency * 0.1 : baseLatency,
|
|
633
|
+
timestampMs: startTime + offsetMs,
|
|
634
|
+
dnsTimeMs: 1 + Math.random() * 0.9,
|
|
635
|
+
tcpTimeMs: 4 + Math.random() * 1.5,
|
|
636
|
+
tlsTimeMs: 6 + Math.random() * 1.8,
|
|
637
|
+
statusCode,
|
|
638
|
+
errorMessage
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
return data.sort((a, b) => a.timestampMs - b.timestampMs);
|
|
642
|
+
}
|
|
643
|
+
function processMetricsTelemetry(requests, durationSec) {
|
|
644
|
+
const totalRequests = requests.length;
|
|
645
|
+
const durations = requests.map((r) => r.durationMs);
|
|
646
|
+
const avgLatencyMs = mathUtils.mean(durations);
|
|
647
|
+
const stdDevMs = mathUtils.stdDev(durations, avgLatencyMs);
|
|
648
|
+
const p95LatencyMs = mathUtils.percentile(durations, 95);
|
|
649
|
+
const avgDnsMs = mathUtils.mean(requests.map((r) => r.dnsTimeMs || 0));
|
|
650
|
+
const avgTcpMs = mathUtils.mean(requests.map((r) => r.tcpTimeMs || 0));
|
|
651
|
+
const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
|
|
652
|
+
const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
|
|
653
|
+
let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
|
|
654
|
+
const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
|
|
655
|
+
[400, 401, 403, 404, 429, 500, 502, 503, 504].forEach((c) => codeCounts[c] = 0);
|
|
656
|
+
requests.forEach((r) => {
|
|
657
|
+
if (r.statusCode >= 100 && r.statusCode < 200) c1xx++;
|
|
658
|
+
else if (r.statusCode >= 200 && r.statusCode < 300) c2xx++;
|
|
659
|
+
else if (r.statusCode >= 300 && r.statusCode < 400) c3xx++;
|
|
660
|
+
else if (r.statusCode >= 400 && r.statusCode < 500) c4xx++;
|
|
661
|
+
else if (r.statusCode >= 500) c5xx++;
|
|
662
|
+
if (r.statusCode in codeCounts) {
|
|
663
|
+
codeCounts[r.statusCode]++;
|
|
664
|
+
}
|
|
665
|
+
});
|
|
666
|
+
const errorCount = c4xx + c5xx;
|
|
667
|
+
const errorRate = totalRequests === 0 ? 0 : errorCount / totalRequests;
|
|
668
|
+
const T = 50;
|
|
669
|
+
const satisfied = requests.filter((r) => r.durationMs <= T && r.statusCode < 400).length;
|
|
670
|
+
const tolerating = requests.filter((r) => r.durationMs > T && r.durationMs <= T * 4 && r.statusCode < 400).length;
|
|
671
|
+
const apdexScore = totalRequests === 0 ? 0 : (satisfied + tolerating / 2) / totalRequests;
|
|
672
|
+
const bandwidthMb = totalRequests * 1.2 / durationSec / 10;
|
|
673
|
+
return {
|
|
674
|
+
totalRequests,
|
|
675
|
+
requestsPerSecond: totalRequests / durationSec,
|
|
676
|
+
avgLatencyMs,
|
|
677
|
+
stdDevMs,
|
|
678
|
+
p95LatencyMs,
|
|
679
|
+
errorRate,
|
|
680
|
+
apdexScore,
|
|
681
|
+
c1xx,
|
|
682
|
+
c2xx,
|
|
683
|
+
c3xx,
|
|
684
|
+
c4xx,
|
|
685
|
+
c5xx,
|
|
686
|
+
avgDnsMs,
|
|
687
|
+
avgTcpMs,
|
|
688
|
+
avgTlsMs,
|
|
689
|
+
avgTtfbMs,
|
|
690
|
+
bandwidthMb,
|
|
691
|
+
codeCounts
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
function printMatrixDashboard(m, threads) {
|
|
695
|
+
const pad = (val, size) => String(val).padEnd(size).substring(0, size);
|
|
696
|
+
console.log(`-----------------------------------------------------------------------------------------`);
|
|
697
|
+
console.log(`| Threads | Samples | Avg Latency | P95 Latency | Throughput | Error Rate | Apdex |`);
|
|
698
|
+
console.log(`-----------------------------------------------------------------------------------------`);
|
|
699
|
+
console.log(`| ${pad(threads, 7)} | ${pad(m.totalRequests, 9)} | ${pad(m.avgLatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.p95LatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.requestsPerSecond.toFixed(0) + "/s", 10)} | ${pad((m.errorRate * 100).toFixed(2) + "%", 10)} | ${pad(m.apdexScore.toFixed(2), 9)} |`);
|
|
700
|
+
console.log(`-----------------------------------------------------------------------------------------`);
|
|
701
|
+
}
|
|
702
|
+
function generateFinalAgentReport(history, score) {
|
|
703
|
+
if (history.length === 0) return;
|
|
704
|
+
const peakThroughput = Math.max(...history.map((h) => h.throughput));
|
|
705
|
+
const cleanRuns = history.filter((h) => h.apdex >= 0.85 && h.errorRate <= 0.01);
|
|
706
|
+
const maxSafeThreads = cleanRuns.length > 0 ? cleanRuns[cleanRuns.length - 1].threads : history[0].threads;
|
|
707
|
+
console.log("\n=======================================================");
|
|
708
|
+
console.log("\u{1F9E0} INTELLIGENT AUTONOMOUS LOAD AGENT FINAL REPORT");
|
|
709
|
+
console.log("=======================================================");
|
|
710
|
+
console.log(`\u{1F49A} Unified Blaze Health Score : ${score}/100`);
|
|
711
|
+
console.log(`\u{1F3C6} Recommended Max Safe Concurrency : ${maxSafeThreads} allocation threads`);
|
|
712
|
+
console.log(`\u{1F4A5} Infrastructure Failure Boundary : ${history[history.length - 1].threads} concurrency allocation`);
|
|
713
|
+
console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
|
|
714
|
+
console.log("=======================================================\n");
|
|
715
|
+
}
|
|
670
716
|
function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], geminiAnalysisHtml = "", baselineData = null) {
|
|
671
717
|
const summaryArtifactPath = config.outputHtml.replace(/\.html$/, "-summary.json");
|
|
672
718
|
try {
|
|
@@ -1157,7 +1203,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1157
1203
|
const feedback = document.getElementById('nlpQueryFeedback');
|
|
1158
1204
|
|
|
1159
1205
|
let filterFn = (row, data) => true;
|
|
1160
|
-
const numMatch = q.match(/(
|
|
1206
|
+
const numMatch = q.match(/(d+(?:.d+)?)/);
|
|
1161
1207
|
const targetNum = numMatch ? parseFloat(numMatch[1]) : null;
|
|
1162
1208
|
|
|
1163
1209
|
const isGreater = q.includes('>') || q.includes('greater') || q.includes('above') || q.includes('more');
|
|
@@ -1385,69 +1431,14 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1385
1431
|
</script></body></html>`;
|
|
1386
1432
|
try {
|
|
1387
1433
|
fs.writeFileSync(config.outputHtml, htmlContent, "utf-8");
|
|
1388
|
-
console.log(
|
|
1434
|
+
console.log(`\u{1F4CA} Standalone Dashboard exported successfully to: ${path.resolve(config.outputHtml)}`);
|
|
1389
1435
|
} catch (err) {
|
|
1390
|
-
console.error(
|
|
1436
|
+
console.error(`\u274C Failed to write HTML output dashboard: ${err}`);
|
|
1391
1437
|
}
|
|
1392
1438
|
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
const parsedRampDown = parseInt(options.rampDown, 10) || 0;
|
|
1400
|
-
const parsedApdex = parseFloat(options.targetApdex);
|
|
1401
|
-
const parsedError = parseFloat(options.targetError);
|
|
1402
|
-
const parsedMaxThreads = parseInt(options.maxThreads, 10);
|
|
1403
|
-
const parsedMaxLatencyRaw = parseFloat(options.maxAllowedLatency || "800");
|
|
1404
|
-
const parsedMaxLatency = parsedMaxLatencyRaw < 50 ? parsedMaxLatencyRaw * 1e3 : parsedMaxLatencyRaw;
|
|
1405
|
-
const config = {
|
|
1406
|
-
targetScript: path.resolve(script || "."),
|
|
1407
|
-
isAgentic: !!options.agentic,
|
|
1408
|
-
isCorrelate: !!options.correlate,
|
|
1409
|
-
isSeo: !!options.seo,
|
|
1410
|
-
clusterLogs: options.clusterLogs !== false,
|
|
1411
|
-
threads: parsedThreads,
|
|
1412
|
-
durationSec: parsedDuration,
|
|
1413
|
-
rampUpSec: parsedRampUp,
|
|
1414
|
-
rampDownSec: parsedRampDown,
|
|
1415
|
-
targetApdex: parsedApdex,
|
|
1416
|
-
targetErrorRate: parsedError,
|
|
1417
|
-
maxThreads: parsedMaxThreads,
|
|
1418
|
-
stepSize: 15,
|
|
1419
|
-
maxAllowedLatencyMs: parsedMaxLatency,
|
|
1420
|
-
outputHtml: options.output,
|
|
1421
|
-
baselinePath: options.baseline || null
|
|
1422
|
-
};
|
|
1423
|
-
if (options.regions) {
|
|
1424
|
-
console.log(chalk.blue(`
|
|
1425
|
-
Initializing Blaze load test simulation against ${chalk.bold(options.url)}...`));
|
|
1426
|
-
console.log(chalk.gray(`Target regions: ${options.regions}`));
|
|
1427
|
-
console.log(chalk.gray(`Duration: ${options.duration}s
|
|
1428
|
-
`));
|
|
1429
|
-
await new Promise((resolve2) => {
|
|
1430
|
-
setTimeout(() => {
|
|
1431
|
-
const allRegions = getMockRegionalData();
|
|
1432
|
-
const requestedZones = options.regions.split(",").map((z) => z.trim());
|
|
1433
|
-
const filteredRegions = allRegions.filter((r) => requestedZones.includes(r.zone));
|
|
1434
|
-
renderGeographicHeatmap(filteredRegions.length > 0 ? filteredRegions : allRegions);
|
|
1435
|
-
console.log(chalk.gray(`Executing localized system stress verification pipelines...
|
|
1436
|
-
`));
|
|
1437
|
-
resolve2(null);
|
|
1438
|
-
}, 1e3);
|
|
1439
|
-
});
|
|
1440
|
-
}
|
|
1441
|
-
if (config.isCorrelate) {
|
|
1442
|
-
await executeTestPipeline(config);
|
|
1443
|
-
}
|
|
1444
|
-
if (config.isAgentic) {
|
|
1445
|
-
await runIntelligentAgenticStressTest(config);
|
|
1446
|
-
} else {
|
|
1447
|
-
await runStandardStressTest(config);
|
|
1448
|
-
}
|
|
1449
|
-
});
|
|
1450
|
-
program.command("heatmap").description("Display real-time geographic latency heatmap breakdown").action(() => {
|
|
1451
|
-
renderGeographicHeatmap(getMockRegionalData());
|
|
1452
|
-
});
|
|
1453
|
-
program.parse(process.argv);
|
|
1439
|
+
function printUsage() {
|
|
1440
|
+
console.log(`Blaze Core Performance Test CLI Engine
|
|
1441
|
+
Options:
|
|
1442
|
+
--threads <count> | --duration <seconds> | --baseline <json_file> | --output <html_file>`);
|
|
1443
|
+
}
|
|
1444
|
+
main().catch(console.error);
|
|
Binary file
|