mirror of
https://github.com/ruvnet/RuView
synced 2026-08-05 19:41:44 +00:00
feat: vendor midstream and sublinear-time-solver libraries
Add ruvnet/midstream (AIMDS real-time inference) and ruvnet/sublinear-time-solver (sublinear optimization algorithms) as vendored dependencies under vendor/. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
+255
@@ -0,0 +1,255 @@
|
||||
#!/usr/bin/env tsx
|
||||
/**
|
||||
* Load Testing Script for AIMDS Gateway
|
||||
*
|
||||
* Simulates realistic load patterns and measures performance metrics
|
||||
*/
|
||||
|
||||
import http from 'http';
|
||||
import { performance } from 'perf_hooks';
|
||||
|
||||
interface LoadTestConfig {
|
||||
baseUrl: string;
|
||||
totalRequests: number;
|
||||
concurrency: number;
|
||||
rampUpSeconds: number;
|
||||
}
|
||||
|
||||
interface RequestResult {
|
||||
success: boolean;
|
||||
latency: number;
|
||||
statusCode?: number;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
interface LoadTestResults {
|
||||
totalRequests: number;
|
||||
successfulRequests: number;
|
||||
failedRequests: number;
|
||||
totalDuration: number;
|
||||
requestsPerSecond: number;
|
||||
latencyStats: {
|
||||
min: number;
|
||||
max: number;
|
||||
mean: number;
|
||||
p50: number;
|
||||
p95: number;
|
||||
p99: number;
|
||||
};
|
||||
}
|
||||
|
||||
class LoadTester {
|
||||
private config: LoadTestConfig;
|
||||
private results: RequestResult[] = [];
|
||||
|
||||
constructor(config: LoadTestConfig) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
async run(): Promise<LoadTestResults> {
|
||||
console.log('🚀 Starting load test...');
|
||||
console.log(` Target: ${this.config.baseUrl}`);
|
||||
console.log(` Total requests: ${this.config.totalRequests}`);
|
||||
console.log(` Concurrency: ${this.config.concurrency}`);
|
||||
console.log(` Ramp-up: ${this.config.rampUpSeconds}s\n`);
|
||||
|
||||
const startTime = performance.now();
|
||||
|
||||
await this.executeLoadTest();
|
||||
|
||||
const endTime = performance.now();
|
||||
const totalDuration = endTime - startTime;
|
||||
|
||||
return this.calculateResults(totalDuration);
|
||||
}
|
||||
|
||||
private async executeLoadTest(): Promise<void> {
|
||||
const batchSize = this.config.concurrency;
|
||||
const numBatches = Math.ceil(this.config.totalRequests / batchSize);
|
||||
const delayBetweenBatches = (this.config.rampUpSeconds * 1000) / numBatches;
|
||||
|
||||
for (let batch = 0; batch < numBatches; batch++) {
|
||||
const batchRequests = Math.min(
|
||||
batchSize,
|
||||
this.config.totalRequests - batch * batchSize
|
||||
);
|
||||
|
||||
const promises: Promise<RequestResult>[] = [];
|
||||
|
||||
for (let i = 0; i < batchRequests; i++) {
|
||||
const requestType = Math.random();
|
||||
|
||||
if (requestType < 0.95) {
|
||||
// 95% fast path requests
|
||||
promises.push(this.makeRequest({
|
||||
action: { type: 'read', resource: '/api/users', method: 'GET' },
|
||||
source: { ip: '192.168.1.1' },
|
||||
}));
|
||||
} else {
|
||||
// 5% deep path requests
|
||||
promises.push(this.makeRequest({
|
||||
action: { type: 'complex_operation' },
|
||||
source: { ip: '192.168.1.1' },
|
||||
behaviorSequence: this.generateBehaviorSequence(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
const batchResults = await Promise.all(promises);
|
||||
this.results.push(...batchResults);
|
||||
|
||||
const progress = ((batch + 1) / numBatches * 100).toFixed(1);
|
||||
process.stdout.write(`\r Progress: ${progress}% (${this.results.length}/${this.config.totalRequests} requests)`);
|
||||
|
||||
if (batch < numBatches - 1) {
|
||||
await this.sleep(delayBetweenBatches);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n');
|
||||
}
|
||||
|
||||
private async makeRequest(payload: any): Promise<RequestResult> {
|
||||
const startTime = performance.now();
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const data = JSON.stringify(payload);
|
||||
|
||||
const options = {
|
||||
hostname: 'localhost',
|
||||
port: 3000,
|
||||
path: '/api/v1/defend',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': data.length,
|
||||
},
|
||||
};
|
||||
|
||||
const req = http.request(options, (res) => {
|
||||
let responseData = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
responseData += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
const latency = performance.now() - startTime;
|
||||
resolve({
|
||||
success: res.statusCode === 200,
|
||||
latency,
|
||||
statusCode: res.statusCode,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (error) => {
|
||||
const latency = performance.now() - startTime;
|
||||
resolve({
|
||||
success: false,
|
||||
latency,
|
||||
error: error.message,
|
||||
});
|
||||
});
|
||||
|
||||
req.write(data);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
private generateBehaviorSequence(): number[] {
|
||||
const length = 5;
|
||||
return Array.from({ length }, () => Math.random());
|
||||
}
|
||||
|
||||
private calculateResults(totalDuration: number): LoadTestResults {
|
||||
const successful = this.results.filter(r => r.success);
|
||||
const latencies = successful.map(r => r.latency).sort((a, b) => a - b);
|
||||
|
||||
const sum = latencies.reduce((a, b) => a + b, 0);
|
||||
const mean = sum / latencies.length;
|
||||
|
||||
return {
|
||||
totalRequests: this.results.length,
|
||||
successfulRequests: successful.length,
|
||||
failedRequests: this.results.length - successful.length,
|
||||
totalDuration,
|
||||
requestsPerSecond: (this.results.length / totalDuration) * 1000,
|
||||
latencyStats: {
|
||||
min: latencies[0] || 0,
|
||||
max: latencies[latencies.length - 1] || 0,
|
||||
mean,
|
||||
p50: latencies[Math.floor(latencies.length * 0.5)] || 0,
|
||||
p95: latencies[Math.floor(latencies.length * 0.95)] || 0,
|
||||
p99: latencies[Math.floor(latencies.length * 0.99)] || 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private sleep(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
}
|
||||
|
||||
function printResults(results: LoadTestResults): void {
|
||||
console.log('📊 Load Test Results\n');
|
||||
console.log('Overall:');
|
||||
console.log(` Total requests: ${results.totalRequests}`);
|
||||
console.log(` Successful: ${results.successfulRequests} (${(results.successfulRequests / results.totalRequests * 100).toFixed(1)}%)`);
|
||||
console.log(` Failed: ${results.failedRequests} (${(results.failedRequests / results.totalRequests * 100).toFixed(1)}%)`);
|
||||
console.log(` Total duration: ${results.totalDuration.toFixed(0)}ms`);
|
||||
console.log(` Throughput: ${results.requestsPerSecond.toFixed(0)} req/s`);
|
||||
console.log('');
|
||||
console.log('Latency (ms):');
|
||||
console.log(` Min: ${results.latencyStats.min.toFixed(2)}`);
|
||||
console.log(` Mean: ${results.latencyStats.mean.toFixed(2)}`);
|
||||
console.log(` p50: ${results.latencyStats.p50.toFixed(2)}`);
|
||||
console.log(` p95: ${results.latencyStats.p95.toFixed(2)}`);
|
||||
console.log(` p99: ${results.latencyStats.p99.toFixed(2)}`);
|
||||
console.log(` Max: ${results.latencyStats.max.toFixed(2)}`);
|
||||
console.log('');
|
||||
|
||||
// Performance targets
|
||||
console.log('Target Validation:');
|
||||
const throughputOk = results.requestsPerSecond >= 10000;
|
||||
const p95Ok = results.latencyStats.p95 < 35;
|
||||
const p99Ok = results.latencyStats.p99 < 100;
|
||||
const errorRateOk = (results.failedRequests / results.totalRequests) < 0.01;
|
||||
|
||||
console.log(` Throughput ≥10,000 req/s: ${throughputOk ? '✅' : '❌'} (${results.requestsPerSecond.toFixed(0)})`);
|
||||
console.log(` p95 latency <35ms: ${p95Ok ? '✅' : '❌'} (${results.latencyStats.p95.toFixed(2)}ms)`);
|
||||
console.log(` p99 latency <100ms: ${p99Ok ? '✅' : '❌'} (${results.latencyStats.p99.toFixed(2)}ms)`);
|
||||
console.log(` Error rate <1%: ${errorRateOk ? '✅' : '❌'} (${(results.failedRequests / results.totalRequests * 100).toFixed(2)}%)`);
|
||||
}
|
||||
|
||||
// Main execution
|
||||
async function main() {
|
||||
const config: LoadTestConfig = {
|
||||
baseUrl: 'http://localhost:3000',
|
||||
totalRequests: parseInt(process.env.LOAD_TEST_REQUESTS || '1000'),
|
||||
concurrency: parseInt(process.env.LOAD_TEST_CONCURRENCY || '50'),
|
||||
rampUpSeconds: parseInt(process.env.LOAD_TEST_RAMP_UP || '5'),
|
||||
};
|
||||
|
||||
const tester = new LoadTester(config);
|
||||
const results = await tester.run();
|
||||
printResults(results);
|
||||
|
||||
// Exit with error code if targets not met
|
||||
const allTargetsMet =
|
||||
results.requestsPerSecond >= 10000 &&
|
||||
results.latencyStats.p95 < 35 &&
|
||||
results.latencyStats.p99 < 100 &&
|
||||
(results.failedRequests / results.totalRequests) < 0.01;
|
||||
|
||||
process.exit(allTargetsMet ? 0 : 1);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main().catch(error => {
|
||||
console.error('❌ Load test failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export { LoadTester, LoadTestConfig, LoadTestResults };
|
||||
Reference in New Issue
Block a user