perf: 1200장수 용량 fixture와 E1 profile을 추가

900 NPC와 300 synthetic 사용자를 5분 턴으로 처리하는 결정론적 engine profile을 추가하고, 전용 PostgreSQL schema와 Redis DB에 300개 인증 session을 준비하는 격리 load fixture를 제공한다.
This commit is contained in:
2026-08-16 18:31:14 +00:00
parent 299470590a
commit 7e099326ec
16 changed files with 808 additions and 39 deletions
+35 -8
View File
@@ -2,35 +2,41 @@ import { mkdir, writeFile } from 'node:fs/promises';
import path from 'node:path';
import { assertRuntimeMetadataFinalized, loadConfig, loadTokens } from './config.js';
import { cleanupCapacityFixture, seedCapacityFixture, verifyCapacityFixture } from './fixture.js';
import { describeDryRun, runLoadTest } from './runner.js';
type Command = 'run' | 'dry-run' | 'validate';
type Command = 'run' | 'dry-run' | 'validate' | 'seed' | 'verify-fixture' | 'cleanup';
const usage = (): never => {
process.stderr.write('usage: cli.ts <validate|dry-run|run> --config <file> [--tokens <0600-gitignored-file>] [--output <new-json-file>]\n');
process.stderr.write('usage: cli.ts <validate|dry-run|run|seed|verify-fixture|cleanup> --config <file> [--tokens <0600-gitignored-file>] [--output <new-json-file>] [--confirm <load_schema>]\n');
process.exit(64);
};
const parseArguments = (argv: readonly string[]): { command: Command; config: string; tokens?: string; output?: string } => {
const parseArguments = (argv: readonly string[]): { command: Command; config: string; tokens?: string; output?: string; confirm?: string } => {
const command = argv[0];
if (!['run', 'dry-run', 'validate'].includes(command ?? '')) usage();
if (!['run', 'dry-run', 'validate', 'seed', 'verify-fixture', 'cleanup'].includes(command ?? '')) usage();
const values = new Map<string, string>();
for (let index = 1; index < argv.length; index += 2) {
const flag = argv[index];
const value = argv[index + 1];
if (!flag || !['--config', '--tokens', '--output'].includes(flag) || !value) usage();
if (!flag || !['--config', '--tokens', '--output', '--confirm'].includes(flag) || !value) usage();
values.set(flag, value);
}
const config = values.get('--config');
if (!config) usage();
if (command === 'run' && (!values.get('--tokens') || !values.get('--output'))) usage();
if (command === 'validate' && (values.has('--tokens') || values.has('--output'))) usage();
if (command === 'dry-run' && values.has('--output')) usage();
if (command === 'seed' && (!values.get('--tokens') || values.has('--output') || values.has('--confirm'))) usage();
if (command === 'cleanup' && (!values.get('--confirm') || values.has('--tokens') || values.has('--output'))) usage();
if (command === 'verify-fixture' && (values.has('--tokens') || values.has('--output') || values.has('--confirm'))) usage();
if (command === 'validate' && (values.has('--tokens') || values.has('--output') || values.has('--confirm'))) usage();
if (command === 'dry-run' && (values.has('--output') || values.has('--confirm'))) usage();
if (command === 'run' && values.has('--confirm')) usage();
return {
command: command as Command,
config: config!,
...(values.get('--tokens') ? { tokens: values.get('--tokens')! } : {}),
...(values.get('--output') ? { output: values.get('--output')! } : {}),
...(values.get('--confirm') ? { confirm: values.get('--confirm')! } : {}),
};
};
@@ -42,6 +48,23 @@ const main = async (): Promise<void> => {
process.stdout.write(`${JSON.stringify({ valid: true, name: config.name, configSha256: sha256 })}\n`);
return;
}
if (args.command === 'seed') {
const result = await seedCapacityFixture({
config,
tokenPath: args.tokens!,
workspaceRoot,
});
process.stdout.write(`${JSON.stringify(result)}\n`);
return;
}
if (args.command === 'verify-fixture') {
process.stdout.write(`${JSON.stringify(await verifyCapacityFixture(config))}\n`);
return;
}
if (args.command === 'cleanup') {
process.stdout.write(`${JSON.stringify(await cleanupCapacityFixture(config, args.confirm!))}\n`);
return;
}
const tokens = args.tokens ? await loadTokens(args.tokens, workspaceRoot, config.capacity.authenticatedViewers) : null;
if (args.command === 'dry-run') {
process.stdout.write(`${JSON.stringify({ valid: true, tokenFileValidated: tokens !== null, configSha256: sha256, plan: describeDryRun(config) }, null, 2)}\n`);
@@ -57,7 +80,11 @@ const main = async (): Promise<void> => {
};
main().catch((error: unknown) => {
const message = error instanceof Error ? error.message : 'unknown load-test error';
let message = error instanceof Error ? error.message : 'unknown load-test error';
for (const secret of [process.env.LOAD_TEST_DATABASE_URL, process.env.LOAD_TEST_REDIS_URL]) {
if (secret) message = message.replaceAll(secret, '[redacted-url]');
}
message = message.replace(/(?:postgres(?:ql)?|rediss?):\/\/[^\s]+/giu, '[redacted-url]');
process.stderr.write(`${message}\n`);
process.exitCode = 1;
});