vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
391 lines (390 loc) • 15 kB
JavaScript
import { PerformanceMonitor } from './performance-monitor.js';
import { AppError } from '../../../utils/errors.js';
import logger from '../../../logger.js';
export class PerformanceBenchmarks {
static instance = null;
config;
performanceMonitor;
operations = new Map();
results = new Map();
baselines = new Map();
constructor(config) {
this.config = config;
this.performanceMonitor = PerformanceMonitor.getInstance();
this.initializeDefaultOperations();
logger.info({ config }, 'Performance Benchmarks initialized');
}
static getInstance(config) {
if (!PerformanceBenchmarks.instance) {
if (!config) {
throw new AppError('Benchmark configuration required for first initialization');
}
PerformanceBenchmarks.instance = new PerformanceBenchmarks(config);
}
return PerformanceBenchmarks.instance;
}
initializeDefaultOperations() {
this.registerOperation({
name: 'task_creation',
category: 'task_management',
targetTime: 10,
description: 'Create a new task',
operation: async () => {
const start = performance.now();
await new Promise(resolve => setTimeout(resolve, 1));
return performance.now() - start;
}
});
this.registerOperation({
name: 'task_listing',
category: 'task_management',
targetTime: 20,
description: 'List tasks with filtering',
operation: async () => {
const start = performance.now();
await new Promise(resolve => setTimeout(resolve, 2));
return performance.now() - start;
}
});
this.registerOperation({
name: 'task_execution',
category: 'execution',
targetTime: 50,
description: 'Execute a task',
operation: async () => {
const start = performance.now();
await new Promise(resolve => setTimeout(resolve, 5));
return performance.now() - start;
}
});
this.registerOperation({
name: 'status_checking',
category: 'task_management',
targetTime: 15,
description: 'Check task status',
operation: async () => {
const start = performance.now();
await new Promise(resolve => setTimeout(resolve, 1));
return performance.now() - start;
}
});
this.registerOperation({
name: 'storage_read',
category: 'storage',
targetTime: 10,
description: 'Read from storage',
operation: async () => {
const start = performance.now();
await new Promise(resolve => setTimeout(resolve, 1));
return performance.now() - start;
}
});
this.registerOperation({
name: 'storage_write',
category: 'storage',
targetTime: 15,
description: 'Write to storage',
operation: async () => {
const start = performance.now();
await new Promise(resolve => setTimeout(resolve, 2));
return performance.now() - start;
}
});
this.registerOperation({
name: 'cache_get',
category: 'cache',
targetTime: 5,
description: 'Get from cache',
operation: async () => {
const start = performance.now();
await new Promise(resolve => setTimeout(resolve, 0.5));
return performance.now() - start;
}
});
this.registerOperation({
name: 'cache_set',
category: 'cache',
targetTime: 8,
description: 'Set cache value',
operation: async () => {
const start = performance.now();
await new Promise(resolve => setTimeout(resolve, 1));
return performance.now() - start;
}
});
this.registerOperation({
name: 'memory_allocation',
category: 'memory',
targetTime: 5,
description: 'Memory allocation and cleanup',
operation: async () => {
const start = performance.now();
const data = new Array(1000).fill(0);
data.length = 0;
return performance.now() - start;
}
});
logger.info(`Initialized ${this.operations.size} default benchmark operations`);
}
registerOperation(operation) {
this.operations.set(operation.name, operation);
logger.debug({ operationName: operation.name }, 'Benchmark operation registered');
}
async runBenchmark(operationName) {
const operation = this.operations.get(operationName);
if (!operation) {
throw new AppError(`Benchmark operation not found: ${operationName}`);
}
logger.info({ operationName }, 'Running benchmark');
if (operation.setup) {
await operation.setup();
}
try {
for (let i = 0; i < this.config.warmupIterations; i++) {
await operation.operation();
}
const times = [];
const memoryUsages = [];
for (let i = 0; i < this.config.iterations; i++) {
const initialMemory = process.memoryUsage().heapUsed;
const operationId = `benchmark_${operationName}_${i}`;
this.performanceMonitor.startOperation(operationId);
await operation.operation();
const duration = this.performanceMonitor.endOperation(operationId, {
benchmark: true,
iteration: i,
operationName
});
const finalMemory = process.memoryUsage().heapUsed;
const memoryUsed = (finalMemory - initialMemory) / 1024 / 1024;
times.push(duration);
memoryUsages.push(memoryUsed);
}
const averageTime = times.reduce((sum, time) => sum + time, 0) / times.length;
const averageMemory = memoryUsages.reduce((sum, mem) => sum + mem, 0) / memoryUsages.length;
const passed = averageTime <= operation.targetTime;
const baseline = this.baselines.get(operationName);
let improvement;
let regression;
if (baseline) {
const change = ((baseline - averageTime) / baseline) * 100;
if (change > 0) {
improvement = change;
}
else {
regression = Math.abs(change);
}
}
else {
this.baselines.set(operationName, averageTime);
}
const result = {
operationName,
category: operation.category,
targetTime: operation.targetTime,
actualTime: averageTime,
memoryUsed: averageMemory,
iterations: this.config.iterations,
timestamp: new Date(),
passed,
improvement,
regression
};
if (!this.results.has(operationName)) {
this.results.set(operationName, []);
}
this.results.get(operationName).push(result);
const operationResults = this.results.get(operationName);
if (operationResults.length > 100) {
operationResults.splice(0, operationResults.length - 100);
}
logger.info({
operationName,
averageTime: averageTime.toFixed(2),
targetTime: operation.targetTime,
passed,
improvement: improvement?.toFixed(2),
regression: regression?.toFixed(2)
}, 'Benchmark completed');
return result;
}
finally {
if (operation.cleanup) {
await operation.cleanup();
}
}
}
async runAllBenchmarks() {
const results = [];
logger.info(`Running ${this.operations.size} benchmark operations`);
for (const operationName of this.operations.keys()) {
try {
const result = await this.runBenchmark(operationName);
results.push(result);
}
catch (error) {
logger.error({ operationName, error }, 'Benchmark failed');
}
}
return results;
}
async runCategoryBenchmarks(category) {
const results = [];
for (const [name, operation] of this.operations) {
if (operation.category === category) {
try {
const result = await this.runBenchmark(name);
results.push(result);
}
catch (error) {
logger.error({ operationName: name, error }, 'Category benchmark failed');
}
}
}
return results;
}
getBenchmarkResults(operationName, limit) {
const results = this.results.get(operationName) || [];
return limit ? results.slice(-limit) : results;
}
getAllBenchmarkResults() {
return new Map(this.results);
}
detectRegressions() {
if (!this.config.enableRegression) {
return [];
}
const regressions = [];
const now = Date.now();
const baselineWindow = this.config.baselineWindow * 60 * 60 * 1000;
for (const [operationName, results] of this.results) {
if (results.length < 2)
continue;
const baselineResults = results.filter(r => now - r.timestamp.getTime() >= baselineWindow);
const recentResults = results.filter(r => now - r.timestamp.getTime() < baselineWindow);
if (baselineResults.length === 0 || recentResults.length === 0)
continue;
const baselineAvg = baselineResults.reduce((sum, r) => sum + r.actualTime, 0) / baselineResults.length;
const recentAvg = recentResults.reduce((sum, r) => sum + r.actualTime, 0) / recentResults.length;
const regressionPercentage = ((recentAvg - baselineAvg) / baselineAvg) * 100;
if (regressionPercentage > 10) {
let severity = 'low';
let recommendation = 'Monitor performance trends';
if (regressionPercentage > 50) {
severity = 'critical';
recommendation = 'Immediate optimization required';
}
else if (regressionPercentage > 30) {
severity = 'high';
recommendation = 'Performance optimization needed';
}
else if (regressionPercentage > 20) {
severity = 'medium';
recommendation = 'Consider performance improvements';
}
regressions.push({
operationName,
baselineTime: baselineAvg,
currentTime: recentAvg,
regressionPercentage,
severity,
recommendation
});
}
}
return regressions;
}
getPerformanceSummary() {
const latestResults = [];
for (const results of this.results.values()) {
if (results.length > 0) {
latestResults.push(results[results.length - 1]);
}
}
const totalOperations = latestResults.length;
const passedOperations = latestResults.filter(r => r.passed).length;
const failedOperations = totalOperations - passedOperations;
const averagePerformance = totalOperations > 0
? latestResults.reduce((sum, r) => sum + (r.actualTime / r.targetTime), 0) / totalOperations
: 0;
const regressions = this.detectRegressions();
const regressionCount = regressions.length;
let overallHealth = 'excellent';
if (failedOperations > totalOperations * 0.5 || regressions.some(r => r.severity === 'critical')) {
overallHealth = 'critical';
}
else if (failedOperations > totalOperations * 0.3 || regressions.some(r => r.severity === 'high')) {
overallHealth = 'warning';
}
else if (failedOperations > 0 || regressions.length > 0) {
overallHealth = 'good';
}
return {
totalOperations,
passedOperations,
failedOperations,
averagePerformance,
regressionCount,
overallHealth
};
}
updateBaseline(operationName, baselineTime) {
if (baselineTime !== undefined) {
this.baselines.set(operationName, baselineTime);
}
else {
const results = this.results.get(operationName);
if (results && results.length > 0) {
const latestResult = results[results.length - 1];
this.baselines.set(operationName, latestResult.actualTime);
}
}
logger.info({ operationName, baseline: this.baselines.get(operationName) }, 'Baseline updated');
}
clearResults(operationName) {
if (operationName) {
this.results.delete(operationName);
this.baselines.delete(operationName);
logger.info({ operationName }, 'Benchmark results cleared');
}
else {
this.results.clear();
this.baselines.clear();
logger.info('All benchmark results cleared');
}
}
exportResults() {
const operations = Array.from(this.operations.values());
const results = {};
const baselines = {};
for (const [name, resultArray] of this.results) {
results[name] = resultArray;
}
for (const [name, baseline] of this.baselines) {
baselines[name] = baseline;
}
return {
operations,
results,
baselines,
summary: this.getPerformanceSummary(),
regressions: this.detectRegressions()
};
}
}
export const DEFAULT_BENCHMARK_CONFIG = {
iterations: 10,
warmupIterations: 3,
targetOverhead: 50,
memoryThreshold: 100,
enableRegression: true,
baselineWindow: 24
};
export function getPerformanceBenchmarks() {
try {
return PerformanceBenchmarks.getInstance();
}
catch {
return null;
}
}