142 lines
4.3 KiB
JavaScript
142 lines
4.3 KiB
JavaScript
/**
|
|
* Priority 14: Load & Stress Benchmark Runner
|
|
*
|
|
* Simulates high concurrency scenarios:
|
|
* 1. Concurrent Job Discovery searches
|
|
* 2. Concurrent Readiness probe checks
|
|
* 3. Concurrent AI Matching calculations
|
|
*
|
|
* Measures: Requests per second (RPS), Error rate %, and p50, p95, p99 latencies.
|
|
*/
|
|
|
|
const http = require("http");
|
|
|
|
function makeRequest(url, options = {}) {
|
|
return new Promise((resolve) => {
|
|
const start = Date.now();
|
|
const parsed = new URL(url);
|
|
const req = http.request(
|
|
{
|
|
hostname: parsed.hostname,
|
|
port: parsed.port,
|
|
path: parsed.pathname + parsed.search,
|
|
method: options.method || "GET",
|
|
headers: options.headers || {},
|
|
},
|
|
(res) => {
|
|
let body = "";
|
|
res.on("data", (chunk) => (body += chunk));
|
|
res.on("end", () => {
|
|
resolve({
|
|
statusCode: res.statusCode,
|
|
latencyMs: Date.now() - start,
|
|
success: res.statusCode >= 200 && res.statusCode < 400,
|
|
});
|
|
});
|
|
}
|
|
);
|
|
|
|
req.on("error", (err) => {
|
|
resolve({
|
|
statusCode: 0,
|
|
latencyMs: Date.now() - start,
|
|
success: false,
|
|
error: err.message,
|
|
});
|
|
});
|
|
|
|
if (options.body) {
|
|
req.write(options.body);
|
|
}
|
|
req.end();
|
|
});
|
|
}
|
|
|
|
function calculatePercentiles(latencies) {
|
|
if (latencies.length === 0) return { p50: 0, p95: 0, p99: 0, min: 0, max: 0 };
|
|
const sorted = [...latencies].sort((a, b) => a - b);
|
|
const count = sorted.length;
|
|
return {
|
|
count,
|
|
p50: Math.round(sorted[Math.floor(count * 0.5)]),
|
|
p95: Math.round(sorted[Math.floor(count * 0.95)] || sorted[count - 1]),
|
|
p99: Math.round(sorted[Math.floor(count * 0.99)] || sorted[count - 1]),
|
|
min: Math.round(sorted[0]),
|
|
max: Math.round(sorted[count - 1]),
|
|
};
|
|
}
|
|
|
|
async function runScenario(name, url, totalRequests = 200, concurrency = 20) {
|
|
console.log(`\n--- Running Scenario: ${name} (${totalRequests} requests, ${concurrency} concurrency) ---`);
|
|
const latencies = [];
|
|
let successCount = 0;
|
|
let failCount = 0;
|
|
|
|
const startTime = Date.now();
|
|
let completed = 0;
|
|
|
|
async function worker() {
|
|
while (completed < totalRequests) {
|
|
completed++;
|
|
const res = await makeRequest(url);
|
|
latencies.push(res.latencyMs);
|
|
if (res.success) {
|
|
successCount++;
|
|
} else {
|
|
failCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
const workers = Array.from({ length: concurrency }, () => worker());
|
|
await Promise.all(workers);
|
|
|
|
const durationSec = (Date.now() - startTime) / 1000;
|
|
const rps = Math.round(totalRequests / durationSec);
|
|
const p = calculatePercentiles(latencies);
|
|
const errorRate = ((failCount / totalRequests) * 100).toFixed(1);
|
|
|
|
console.log(` Requests Completed: ${totalRequests}`);
|
|
console.log(` Duration: ${durationSec.toFixed(2)}s | Throughput: ${rps} req/sec`);
|
|
console.log(` Error Rate: ${errorRate}% (Success: ${successCount}, Failed: ${failCount})`);
|
|
console.log(` Latency (ms): p50=${p.p50}ms | p95=${p.p95}ms | p99=${p.p99}ms (min=${p.min}ms, max=${p.max}ms)`);
|
|
|
|
return { name, totalRequests, rps, errorRate, ...p };
|
|
}
|
|
|
|
async function main() {
|
|
console.log("=================================================");
|
|
console.log(" JOBSBOARD PRODUCTION LOAD & STRESS BENCHMARK ");
|
|
console.log("=================================================");
|
|
|
|
// Scenario 1: Readiness Health Check (high-frequency load balancer probe)
|
|
const healthResult = await runScenario(
|
|
"Load Balancer Readiness Probe",
|
|
"http://localhost:3000/api/health?type=readiness",
|
|
300,
|
|
30
|
|
);
|
|
|
|
// Scenario 2: Public Job Discovery Search (with filters)
|
|
const searchResult = await runScenario(
|
|
"Marketplace Job Discovery (/api/jobs?search=engineer&remoteOnly=true)",
|
|
"http://localhost:3000/api/jobs?search=engineer&remoteOnly=true&limit=20",
|
|
200,
|
|
20
|
|
);
|
|
|
|
// Scenario 3: Metrics Observability Endpoint
|
|
const metricsResult = await runScenario(
|
|
"Metrics Endpoint (/api/metrics)",
|
|
"http://localhost:3000/api/metrics",
|
|
100,
|
|
10
|
|
);
|
|
|
|
console.log("\n=================================================");
|
|
console.log(" BENCHMARK SUMMARY MATRIX ");
|
|
console.log("=================================================");
|
|
console.log(JSON.stringify({ healthResult, searchResult, metricsResult }, null, 2));
|
|
}
|
|
|
|
main().catch(console.error);
|