Skip to main content
Glama

Claude MCP Server Ecosystem

by Coder-RL
dynamic_memory_load_test.js20.9 kB
#!/usr/bin/env node /** * Dynamic Memory Load Test for MCP Servers * Tests memory efficiency under various load conditions */ import { EventEmitter } from 'events'; import { spawn } from 'child_process'; import fs from 'fs'; import path from 'path'; // Test Configuration const TEST_CONFIG = { duration: 300000, // 5 minutes per test concurrentClients: 10, requestsPerClient: 100, dataPayloadSizes: [1024, 10240, 102400], // 1KB, 10KB, 100KB memoryCheckInterval: 1000, // 1 second gcTriggerInterval: 10000, // 10 seconds maxMemoryThreshold: 500 * 1024 * 1024, // 500MB servers: [ 'data-governance', 'data-pipeline', 'data-warehouse', 'enhanced-memory', 'ml-deployment', 'optimization', 'realtime-analytics', 'security-vulnerability', 'sequential-thinking', 'ui-design' ] }; class MemoryUtils { static formatBytes(bytes) { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(Math.abs(bytes)) / Math.log(k)); const value = bytes / Math.pow(k, i); return `${value.toFixed(2)} ${sizes[i]}`; } static forceGC() { if (global.gc) { global.gc(); return true; } return false; } static getMemoryUsage() { const usage = process.memoryUsage(); return { heap: usage.heapUsed, total: usage.heapTotal, rss: usage.rss, external: usage.external, arrayBuffers: usage.arrayBuffers }; } static async getProcessMemory(pid) { return new Promise((resolve, reject) => { const ps = spawn('ps', ['-o', 'pid,rss,vsz', '-p', pid]); let output = ''; ps.stdout.on('data', (data) => { output += data.toString(); }); ps.on('close', (code) => { if (code !== 0) { reject(new Error(`ps command failed with code ${code}`)); return; } const lines = output.trim().split('\n'); if (lines.length < 2) { reject(new Error('No process data found')); return; } const parts = lines[1].trim().split(/\s+/); resolve({ pid: parseInt(parts[0]), rss: parseInt(parts[1]) * 1024, // Convert KB to bytes vsz: parseInt(parts[2]) * 1024 // Convert KB to bytes }); }); ps.on('error', reject); }); } } class MCPServerMonitor extends EventEmitter { constructor(serverName) { super(); this.serverName = serverName; this.pid = null; this.isMonitoring = false; this.metrics = []; this.alerts = []; this.startTime = null; } async start() { // Get PID from PM2 try { const pm2List = await this.execCommand('pm2 jlist'); const processes = JSON.parse(pm2List); const serverProcess = processes.find(p => p.name === this.serverName); if (!serverProcess || !serverProcess.pid) { throw new Error(`Server ${this.serverName} not found or not running`); } this.pid = serverProcess.pid; this.startTime = Date.now(); this.isMonitoring = true; console.log(`📊 Starting memory monitoring for ${this.serverName} (PID: ${this.pid})`); // Start monitoring this.monitoringInterval = setInterval(() => { this.collectMetrics(); }, TEST_CONFIG.memoryCheckInterval); } catch (error) { console.error(`❌ Failed to start monitoring ${this.serverName}:`, error.message); throw error; } } async collectMetrics() { if (!this.isMonitoring || !this.pid) return; try { const processMemory = await MemoryUtils.getProcessMemory(this.pid); const timestamp = Date.now(); const metric = { timestamp, server: this.serverName, pid: this.pid, memory: { rss: processMemory.rss, vsz: processMemory.vsz, heapPercent: (processMemory.rss / processMemory.vsz) * 100 }, uptime: timestamp - this.startTime }; this.metrics.push(metric); // Check for memory issues this.checkMemoryAlerts(metric); // Emit metric this.emit('metric', metric); // Trim old metrics (keep last 1000) if (this.metrics.length > 1000) { this.metrics.shift(); } } catch (error) { console.error(`❌ Failed to collect metrics for ${this.serverName}:`, error.message); } } checkMemoryAlerts(metric) { const { memory } = metric; // High memory usage alert if (memory.rss > TEST_CONFIG.maxMemoryThreshold) { this.alerts.push({ timestamp: metric.timestamp, type: 'high_memory', severity: 'critical', message: `High memory usage: ${MemoryUtils.formatBytes(memory.rss)}`, data: memory }); console.warn(`⚠️ ${this.serverName}: High memory usage - ${MemoryUtils.formatBytes(memory.rss)}`); } // Memory growth rate alert if (this.metrics.length >= 10) { const recent = this.metrics.slice(-10); const growthRate = this.calculateMemoryGrowthRate(recent); if (growthRate > 10 * 1024 * 1024) { // 10MB per minute this.alerts.push({ timestamp: metric.timestamp, type: 'memory_growth', severity: 'warning', message: `Rapid memory growth: ${MemoryUtils.formatBytes(growthRate)}/min`, data: { growthRate, recentMetrics: recent.length } }); console.warn(`⚠️ ${this.serverName}: Rapid memory growth - ${MemoryUtils.formatBytes(growthRate)}/min`); } } } calculateMemoryGrowthRate(metrics) { if (metrics.length < 2) return 0; const first = metrics[0]; const last = metrics[metrics.length - 1]; const timeDiff = (last.timestamp - first.timestamp) / 1000 / 60; // minutes const memoryDiff = last.memory.rss - first.memory.rss; return timeDiff > 0 ? memoryDiff / timeDiff : 0; } stop() { this.isMonitoring = false; if (this.monitoringInterval) { clearInterval(this.monitoringInterval); } console.log(`📊 Stopped monitoring ${this.serverName}`); } getReport() { if (this.metrics.length === 0) { return { server: this.serverName, status: 'no_data', message: 'No metrics collected' }; } const metrics = this.metrics; const memoryValues = metrics.map(m => m.memory.rss); const report = { server: this.serverName, pid: this.pid, monitoring: { duration: Date.now() - this.startTime, samples: metrics.length, interval: TEST_CONFIG.memoryCheckInterval }, memory: { initial: MemoryUtils.formatBytes(memoryValues[0]), final: MemoryUtils.formatBytes(memoryValues[memoryValues.length - 1]), peak: MemoryUtils.formatBytes(Math.max(...memoryValues)), average: MemoryUtils.formatBytes(memoryValues.reduce((a, b) => a + b, 0) / memoryValues.length), growth: MemoryUtils.formatBytes(memoryValues[memoryValues.length - 1] - memoryValues[0]), stability: this.calculateMemoryStability(memoryValues) }, alerts: { total: this.alerts.length, critical: this.alerts.filter(a => a.severity === 'critical').length, warnings: this.alerts.filter(a => a.severity === 'warning').length, details: this.alerts }, efficiency: { score: this.calculateEfficiencyScore(), rating: this.getEfficiencyRating() } }; return report; } calculateMemoryStability(values) { if (values.length < 2) return 1; const mean = values.reduce((a, b) => a + b, 0) / values.length; const variance = values.reduce((acc, val) => acc + Math.pow(val - mean, 2), 0) / values.length; const stdDev = Math.sqrt(variance); const coefficientOfVariation = mean > 0 ? stdDev / mean : 0; // Higher stability = lower coefficient of variation return Math.max(0, 1 - coefficientOfVariation); } calculateEfficiencyScore() { if (this.metrics.length === 0) return 0; const memoryValues = this.metrics.map(m => m.memory.rss); const stability = this.calculateMemoryStability(memoryValues); const growth = memoryValues[memoryValues.length - 1] - memoryValues[0]; const maxMemory = Math.max(...memoryValues); // Factors: stability (40%), low growth (30%), reasonable peak (30%) const stabilityScore = stability * 0.4; const growthScore = Math.max(0, 1 - (growth / (100 * 1024 * 1024))) * 0.3; // Penalize growth > 100MB const peakScore = Math.max(0, 1 - (maxMemory / TEST_CONFIG.maxMemoryThreshold)) * 0.3; return Math.min(1, stabilityScore + growthScore + peakScore); } getEfficiencyRating() { const score = this.calculateEfficiencyScore(); if (score >= 0.9) return 'Excellent'; if (score >= 0.8) return 'Good'; if (score >= 0.7) return 'Fair'; if (score >= 0.6) return 'Poor'; return 'Critical'; } async execCommand(command) { return new Promise((resolve, reject) => { const child = spawn('sh', ['-c', command]); let output = ''; let error = ''; child.stdout.on('data', (data) => { output += data.toString(); }); child.stderr.on('data', (data) => { error += data.toString(); }); child.on('close', (code) => { if (code !== 0) { reject(new Error(`Command failed: ${error}`)); } else { resolve(output.trim()); } }); }); } } class LoadTester extends EventEmitter { constructor() { super(); this.monitors = new Map(); this.testResults = []; this.isRunning = false; } async runDynamicLoadTest() { console.log('🚀 Starting Dynamic Memory Load Test for MCP Servers'); console.log('===================================================\n'); this.isRunning = true; try { // Initialize monitors for each server for (const serverName of TEST_CONFIG.servers) { const monitor = new MCPServerMonitor(serverName); this.monitors.set(serverName, monitor); monitor.on('metric', (metric) => { this.handleMetric(metric); }); } // Start monitoring all servers console.log('📊 Starting baseline monitoring...\n'); for (const [serverName, monitor] of this.monitors) { try { await monitor.start(); } catch (error) { console.error(`Failed to start monitoring ${serverName}:`, error.message); } } // Wait for baseline console.log('⏱️ Collecting baseline metrics (30 seconds)...\n'); await this.sleep(30000); // Run load tests await this.runLoadTestPhases(); // Final monitoring period console.log('📊 Final monitoring period (30 seconds)...\n'); await this.sleep(30000); // Generate reports await this.generateFinalReport(); } catch (error) { console.error('❌ Load test failed:', error); } finally { this.cleanup(); } } async runLoadTestPhases() { const phases = [ { name: 'Light Load', clients: 3, requests: 50, payloadSize: 1024, duration: 60000 }, { name: 'Medium Load', clients: 7, requests: 100, payloadSize: 10240, duration: 90000 }, { name: 'Heavy Load', clients: 15, requests: 200, payloadSize: 102400, duration: 120000 } ]; for (const phase of phases) { console.log(`🔥 Running ${phase.name} Phase`); console.log(` Clients: ${phase.clients}, Requests: ${phase.requests}, Payload: ${MemoryUtils.formatBytes(phase.payloadSize)}`); console.log(` Duration: ${phase.duration / 1000}s\n`); await this.simulateLoad(phase); // Recovery period console.log('😌 Recovery period (30 seconds)...\n'); await this.sleep(30000); // Force GC on all processes await this.triggerGarbageCollection(); } } async simulateLoad(phase) { const promises = []; // Simulate concurrent clients for (let i = 0; i < phase.clients; i++) { promises.push(this.simulateClient(i, phase)); } // Run for specified duration const timeoutPromise = this.sleep(phase.duration); await Promise.race([ Promise.all(promises), timeoutPromise ]); } async simulateClient(clientId, phase) { const startTime = Date.now(); let requests = 0; while (Date.now() - startTime < phase.duration && requests < phase.requests) { try { // Simulate memory allocation const data = Buffer.alloc(phase.payloadSize, clientId); // Simulate processing const result = await this.processData(data); // Simulate variable delay await this.sleep(Math.random() * 100 + 50); requests++; } catch (error) { console.error(`Client ${clientId} error:`, error.message); } } console.log(` Client ${clientId}: Completed ${requests} requests`); } async processData(data) { // Simulate data processing that might cause memory pressure const processed = []; // Create temporary objects for (let i = 0; i < 10; i++) { processed.push({ id: i, data: data.slice(0, Math.min(data.length, 1024)), timestamp: Date.now(), metadata: { processed: true, size: data.length, checksum: data.reduce((a, b) => a + b, 0) % 1000 } }); } // Simulate async operation await this.sleep(Math.random() * 10); return processed.length; } async triggerGarbageCollection() { console.log('🧹 Triggering garbage collection...\n'); // Try to trigger GC in current process MemoryUtils.forceGC(); // Signal servers to clean up (if they support it) try { for (const serverName of TEST_CONFIG.servers) { await this.execCommand(`pm2 trigger ${serverName} gc 2>/dev/null || true`); } } catch (error) { // Ignore errors - not all servers may support GC triggers } await this.sleep(5000); // Wait for GC to complete } handleMetric(metric) { // Real-time monitoring feedback if (metric.memory.rss > TEST_CONFIG.maxMemoryThreshold * 0.8) { console.log(`⚠️ ${metric.server}: High memory usage - ${MemoryUtils.formatBytes(metric.memory.rss)}`); } } async generateFinalReport() { console.log('\n🔍 Generating Memory Efficiency Report...\n'); console.log('=' .repeat(80)); console.log(' DYNAMIC MEMORY LOAD TEST RESULTS'); console.log('=' .repeat(80)); const reports = []; for (const [serverName, monitor] of this.monitors) { const report = monitor.getReport(); reports.push(report); console.log(`\n📊 ${serverName.toUpperCase()}`); console.log('-' .repeat(50)); if (report.status === 'no_data') { console.log('❌ No data collected'); continue; } console.log(`Memory Usage:`); console.log(` Initial: ${report.memory.initial}`); console.log(` Final: ${report.memory.final}`); console.log(` Peak: ${report.memory.peak}`); console.log(` Average: ${report.memory.average}`); console.log(` Growth: ${report.memory.growth}`); console.log(` Stability: ${(report.memory.stability * 100).toFixed(1)}%`); console.log(`\nEfficiency:`); console.log(` Score: ${(report.efficiency.score * 100).toFixed(1)}%`); console.log(` Rating: ${report.efficiency.rating}`); console.log(`\nAlerts:`); console.log(` Total: ${report.alerts.total}`); console.log(` Critical: ${report.alerts.critical}`); console.log(` Warnings: ${report.alerts.warnings}`); if (report.alerts.critical > 0) { console.log(`\n 🚨 Critical Issues:`); report.alerts.details .filter(a => a.severity === 'critical') .forEach(alert => { console.log(` ${new Date(alert.timestamp).toISOString()}: ${alert.message}`); }); } } // Overall summary console.log('\n' + '=' .repeat(80)); console.log(' SUMMARY'); console.log('=' .repeat(80)); const validReports = reports.filter(r => r.status !== 'no_data'); const avgScore = validReports.reduce((sum, r) => sum + r.efficiency.score, 0) / validReports.length; const totalAlerts = validReports.reduce((sum, r) => sum + r.alerts.total, 0); const criticalAlerts = validReports.reduce((sum, r) => sum + r.alerts.critical, 0); console.log(`\nOverall Performance:`); console.log(` Servers Tested: ${validReports.length}/${TEST_CONFIG.servers.length}`); console.log(` Average Score: ${(avgScore * 100).toFixed(1)}%`); console.log(` Total Alerts: ${totalAlerts}`); console.log(` Critical Issues: ${criticalAlerts}`); console.log(` Test Duration: ${Math.round((Date.now() - this.testStartTime) / 1000 / 60)} minutes`); // Recommendations console.log(`\n📋 Recommendations:`); if (avgScore >= 0.9) { console.log(`✅ Excellent memory efficiency across all servers`); console.log(` All servers are operating within optimal parameters`); } else if (avgScore >= 0.8) { console.log(`✅ Good memory efficiency overall`); console.log(` Consider minor optimizations for peak performance`); } else if (avgScore >= 0.7) { console.log(`⚠️ Fair memory efficiency`); console.log(` Some servers may benefit from memory optimization`); } else { console.log(`❌ Poor memory efficiency detected`); console.log(` Immediate attention required for memory management`); } if (criticalAlerts > 0) { console.log(`🚨 Address ${criticalAlerts} critical memory issues immediately`); } // Save detailed report const detailedReport = { timestamp: new Date().toISOString(), config: TEST_CONFIG, duration: Date.now() - this.testStartTime, summary: { serverseTested: validReports.length, averageScore: avgScore, totalAlerts, criticalAlerts }, servers: reports }; const reportFile = `memory_load_test_${Date.now()}.json`; fs.writeFileSync(reportFile, JSON.stringify(detailedReport, null, 2)); console.log(`\n💾 Detailed report saved: ${reportFile}`); console.log('\n' + '=' .repeat(80)); } cleanup() { this.isRunning = false; for (const [serverName, monitor] of this.monitors) { monitor.stop(); } this.monitors.clear(); console.log('\n🧹 Cleanup completed'); } async sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async execCommand(command) { return new Promise((resolve, reject) => { const child = spawn('sh', ['-c', command]); let output = ''; let error = ''; child.stdout.on('data', (data) => { output += data.toString(); }); child.stderr.on('data', (data) => { error += data.toString(); }); child.on('close', (code) => { if (code !== 0) { reject(new Error(`Command failed: ${error}`)); } else { resolve(output.trim()); } }); }); } } // Main execution async function main() { console.log('🔧 Dynamic Memory Load Test for MCP Servers'); console.log('============================================\n'); if (!global.gc) { console.warn('⚠️ Running without --expose-gc flag'); console.warn(' For accurate GC measurements, run with: node --expose-gc dynamic_memory_load_test.js\n'); } const tester = new LoadTester(); tester.testStartTime = Date.now(); // Handle graceful shutdown process.on('SIGINT', () => { console.log('\n⏹️ Shutting down gracefully...'); tester.cleanup(); process.exit(0); }); try { await tester.runDynamicLoadTest(); console.log('\n✅ Memory load test completed successfully'); process.exit(0); } catch (error) { console.error('\n❌ Memory load test failed:', error); tester.cleanup(); process.exit(1); } } main().catch(console.error);

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Coder-RL/Claude_MCPServer_Dev1'

If you have feedback or need assistance with the MCP directory API, please join our Discord server