Files
cfw-autumn/server/tests/infra/cpuFloodWorker.ts
2026-03-16 20:04:11 +00:00

71 lines
1.6 KiB
TypeScript

/**
* CPU flood worker — connects to the DB through the same path as the app
* (PgBouncer) and runs heavy queries to saturate DB CPU.
* Spawned as a child process by pgFailover.test.ts.
*/
import { sql } from "drizzle-orm";
import { assertNotProductionDb } from "@/db/dbUtils.js";
import { initDrizzle } from "@/db/initDrizzle.js";
assertNotProductionDb();
/** Number of concurrent connections hammering the DB. */
const CONCURRENCY = 50;
const CPU_ROWS = 50_000_000;
let running = true;
let burnCount = 0;
process.on("SIGTERM", () => {
running = false;
});
process.on("SIGINT", () => {
running = false;
});
const { db, client } = initDrizzle({ maxConnections: CONCURRENCY });
// Verify connection
try {
await db.execute(sql`SELECT 1`);
console.log(
`[cpuFloodWorker] Connected via PgBouncer, starting ${CONCURRENCY} concurrent burns`,
);
} catch (err) {
console.error(`[cpuFloodWorker] Failed to connect: ${err}`);
process.exit(1);
}
const burn = async () => {
while (running) {
try {
await db.execute(
sql`SELECT count(*) FROM generate_series(1, ${CPU_ROWS}) AS s WHERE md5(s::text) IS NOT NULL`,
);
burnCount++;
} catch {
// statement_timeout kills it — immediately restart
burnCount++;
}
}
};
const logInterval = setInterval(() => {
if (running) {
console.log(
`[cpuFloodWorker] ${burnCount} burns completed/timed-out so far`,
);
}
}, 5_000);
const promises: Promise<void>[] = [];
for (let i = 0; i < CONCURRENCY; i++) {
promises.push(burn());
}
await Promise.all(promises);
clearInterval(logInterval);
await client.end();
console.log(`[cpuFloodWorker] Stopped (${burnCount} total burns)`);