Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 52 additions & 37 deletions src/testing/bulk-tester.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,9 +337,9 @@ class BulkTester {
});
}

private async startDatabases(): Promise<boolean> {
console.log('\x1b[33mStarting database containers...\x1b[0m');
const result = await this.runCommand('npm', ['run', 'start-dbs', '--', '--cleanup-first']);
private async startDatabases(database: string): Promise<boolean> {
console.log(`\x1b[33mStarting database container for ${database}...\x1b[0m`);
const result = await this.runCommand('npm', ['run', 'start-dbs', '--', '--cleanup-first', `--databases=${database}`]);

if (result.success) {
console.log('\x1b[32mDatabases started successfully\x1b[0m');
Expand All @@ -358,13 +358,24 @@ class BulkTester {
console.log('\x1b[32mDatabases stopped\x1b[0m');
}

private async runDataGeneration(size: number): Promise<{ success: boolean; time: number; error?: string }> {
console.log(`\x1b[34mGenerating ${this.formatNumber(size)} records...\x1b[0m`);
private async killDatabase(database: string): Promise<void> {
const name = database === 'clickhouse' ? 'clickhouse-server'
: database === 'postgresql' ? 'postgres'
: database === 'postgresql-indexed' ? 'postgres-indexed'
: '';
if (!name) return;
console.log(`\x1b[33mStopping ${database} container...\x1b[0m`);
await this.runCommand('docker', ['rm', '-f', name]);
console.log(`\x1b[32m${database} stopped\x1b[0m`);
}

private async runDataGeneration(size: number, database: string): Promise<{ success: boolean; time: number; error?: string }> {
console.log(`\x1b[34mGenerating ${this.formatNumber(size)} records for ${database}...\x1b[0m`);

// Set environment variable for this test
process.env.DATASET_SIZE = size.toString();

const result = await this.runCommand('npm', ['start'], true); // Filter verbose output
const result = await this.runCommand('npm', ['start', '--', `--databases=${database}`], true); // Filter verbose output

if (result.success) {
console.log(`\x1b[32mData generation completed in ${this.formatTime(result.time)}\x1b[0m`);
Expand All @@ -375,10 +386,10 @@ class BulkTester {
return result;
}

private async runQueryTest(): Promise<{ success: boolean; time: number; error?: string }> {
console.log(`\x1b[35mRunning query tests (${this.config.timeLimit}min limit, databases: ${this.config.databases.join(',')})...\x1b[0m`);
private async runQueryTest(database: string): Promise<{ success: boolean; time: number; error?: string }> {
console.log(`\x1b[35mRunning query tests (${this.config.timeLimit}min limit) for ${database}...\x1b[0m`);

const result = await this.runCommand('npm', ['run', 'query-test', '--', `--time-limit=${this.config.timeLimit}`, `--databases=${this.config.databases.join(',')}`], true); // Filter verbose output
const result = await this.runCommand('npm', ['run', 'query-test', '--', `--time-limit=${this.config.timeLimit}`, `--databases=${database}`], true); // Filter verbose output

if (result.success) {
console.log(`\x1b[32mQuery tests completed in ${this.formatTime(result.time)}\x1b[0m`);
Expand Down Expand Up @@ -572,42 +583,46 @@ class BulkTester {
timestamp: new Date().toISOString()
};

// Step 1: Start databases
if (!await this.startDatabases()) {
testResult.error = 'Failed to start databases';
this.session.results.push(testResult);
this.saveSession();
continue;
}
// Serialize per database: start -> load -> query -> kill
for (const database of this.config.databases) {
if (shutdownRequested) break;

try {
// Step 2: Generate data
const dataResult = await this.runDataGeneration(size);
testResult.dataGenTime = dataResult.time;

if (!dataResult.success) {
testResult.error = `Data generation failed: ${dataResult.error}`;
// Step 1: Start selected database
if (!await this.startDatabases(database)) {
testResult.error = `Failed to start database: ${database}`;
this.session.results.push(testResult);
this.saveSession();
continue;
}

// Step 3: Run query tests
const queryResult = await this.runQueryTest();
testResult.queryTime = queryResult.time;

if (!queryResult.success) {
testResult.error = `Query test failed: ${queryResult.error}`;
} else {
testResult.success = true;
try {
// Step 2: Generate data for this database
const dataResult = await this.runDataGeneration(size, database);
testResult.dataGenTime += dataResult.time;

// Step 4: Generate graphs after successful test
await this.generateGraphs();
}
if (!dataResult.success) {
testResult.error = `Data generation failed for ${database}: ${dataResult.error}`;
this.session.results.push(testResult);
this.saveSession();
continue;
}

} finally {
// Step 5: Always kill databases
await this.killDatabases();
// Step 3: Run query tests for this database
const queryResult = await this.runQueryTest(database);
testResult.queryTime += queryResult.time;

if (!queryResult.success) {
testResult.error = `Query test failed for ${database}: ${queryResult.error}`;
} else {
testResult.success = true;

// Step 4: Generate graphs after successful test
await this.generateGraphs();
}
} finally {
// Step 5: Always kill this database only
await this.killDatabase(database);
}
}

this.session.results.push(testResult);
Expand Down
39 changes: 34 additions & 5 deletions src/utils/start-databases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ program
.description('Start ClickHouse and PostgreSQL database containers for performance testing')
.version('1.0.0')
.option('--cleanup-first', 'cleanup existing containers before starting', false)
.option('--databases <databases>', 'comma-separated database types to start (clickhouse,postgresql,postgresql-indexed)')
.addHelpText('after', `

This tool starts the required database containers:
Expand All @@ -31,6 +32,7 @@ Configuration is read from .env file:
Examples:
npm run start-dbs # Start with existing containers
npm run start-dbs -- --cleanup-first # Remove existing containers first
npm run start-dbs -- --databases clickhouse # Start only ClickHouse
`);

// Parse CLI arguments
Expand Down Expand Up @@ -156,7 +158,28 @@ class DatabaseStarter {
console.log(` PostgreSQL: ${postgresMemory} RAM, ${postgresCpus} CPUs`);
console.log(` PostgreSQL (indexed): ${postgresIndexedMemory} RAM, ${postgresIndexedCpus} CPUs\n`);

const configs = this.getContainerConfigs();
let configs = this.getContainerConfigs();

// Optionally filter by databases
if (options.databases) {
const requested = String(options.databases)
.split(',')
.map((d: string) => d.trim().toLowerCase());

const nameMatches = (cfg: ContainerConfig): boolean => {
if (cfg.name === 'clickhouse-server') return requested.includes('clickhouse');
if (cfg.name === 'postgres') return requested.includes('postgresql');
if (cfg.name === 'postgres-indexed') return requested.includes('postgresql-indexed');
return false;
};

configs = configs.filter(nameMatches);

if (configs.length === 0) {
console.error('No valid databases selected for startup.');
process.exit(1);
}
}

try {
// Optionally stop and remove existing containers
Expand All @@ -176,11 +199,17 @@ class DatabaseStarter {
await this.executeCommand(command, `Starting ${config.name}`);
}

console.log('\n🎉 All database containers started successfully!');
console.log('\n🎉 Selected database containers started successfully!');
console.log('\n📋 Container Status:');
console.log(' • ClickHouse: http://localhost:8123');
console.log(` • PostgreSQL (no index): localhost:${postgresPort}`);
console.log(` • PostgreSQL (with index): localhost:${postgresIndexedPort}`);
if (configs.some(c => c.name === 'clickhouse-server')) {
console.log(' • ClickHouse: http://localhost:8123');
}
if (configs.some(c => c.name === 'postgres')) {
console.log(` • PostgreSQL (no index): localhost:${postgresPort}`);
}
if (configs.some(c => c.name === 'postgres-indexed')) {
console.log(` • PostgreSQL (with index): localhost:${postgresIndexedPort}`);
}

console.log('\n⏳ Waiting 10 seconds for containers to be ready...');
await new Promise(resolve => setTimeout(resolve, 10000));
Expand Down