Skip to main content
Glama

CodeGraph CLI MCP Server

by Jakedismo
performance-optimizer.tsโ€ข12.5 kB
import { EventEmitter } from 'events'; import PQueue from 'p-queue'; import LRU from 'lru-cache'; import { createHash } from 'crypto'; export interface OptimizationConfig { maxConcurrency?: number; cacheSize?: number; cacheTTL?: number; batchSize?: number; batchDelay?: number; enableQueryOptimization?: boolean; enableResultCompression?: boolean; } export interface QueryMetrics { hits: number; misses: number; evictions: number; avgResponseTime: number; p95ResponseTime: number; p99ResponseTime: number; } export class PerformanceOptimizer extends EventEmitter { private cache: LRU<string, any>; private queue: PQueue; private batchQueue: Map<string, any[]>; private batchTimers: Map<string, NodeJS.Timeout>; private responseTimes: number[]; private metrics: QueryMetrics; private config: Required<OptimizationConfig>; constructor(config: OptimizationConfig = {}) { super(); this.config = { maxConcurrency: config.maxConcurrency ?? 10, cacheSize: config.cacheSize ?? 1000, cacheTTL: config.cacheTTL ?? 300000, // 5 minutes batchSize: config.batchSize ?? 10, batchDelay: config.batchDelay ?? 100, // 100ms enableQueryOptimization: config.enableQueryOptimization ?? true, enableResultCompression: config.enableResultCompression ?? true }; this.cache = new LRU({ max: this.config.cacheSize, ttl: this.config.cacheTTL, dispose: () => { this.metrics.evictions++; } }); this.queue = new PQueue({ concurrency: this.config.maxConcurrency }); this.batchQueue = new Map(); this.batchTimers = new Map(); this.responseTimes = []; this.metrics = { hits: 0, misses: 0, evictions: 0, avgResponseTime: 0, p95ResponseTime: 0, p99ResponseTime: 0 }; } /** * Execute a query with caching and optimization */ async executeQuery<T>( queryFn: () => Promise<T>, cacheKey: string, options: { skipCache?: boolean; priority?: number; } = {} ): Promise<T> { const startTime = Date.now(); // Check cache first if (!options.skipCache && this.cache.has(cacheKey)) { this.metrics.hits++; this.emit('cache:hit', { key: cacheKey }); return this.cache.get(cacheKey)!; } this.metrics.misses++; this.emit('cache:miss', { key: cacheKey }); // Execute query with concurrency control const result = await this.queue.add( async () => { try { const data = await queryFn(); // Store in cache this.cache.set(cacheKey, data); return data; } catch (error) { this.emit('query:error', { error, cacheKey }); throw error; } }, { priority: options.priority ?? 0 } ); // Record response time const responseTime = Date.now() - startTime; this.recordResponseTime(responseTime); return result as T; } /** * Batch multiple queries for better performance */ async batchQuery<T>( batchKey: string, query: any, executeBatch: (queries: any[]) => Promise<T[]> ): Promise<T> { return new Promise((resolve, reject) => { // Add to batch queue if (!this.batchQueue.has(batchKey)) { this.batchQueue.set(batchKey, []); } const batch = this.batchQueue.get(batchKey)!; const queryIndex = batch.length; batch.push({ query, resolve, reject, index: queryIndex }); // Clear existing timer if (this.batchTimers.has(batchKey)) { clearTimeout(this.batchTimers.get(batchKey)!); } // Execute batch if size limit reached if (batch.length >= this.config.batchSize) { this.executeBatchNow(batchKey, executeBatch); } else { // Set timer for delayed execution const timer = setTimeout(() => { this.executeBatchNow(batchKey, executeBatch); }, this.config.batchDelay); this.batchTimers.set(batchKey, timer); } }); } private async executeBatchNow<T>( batchKey: string, executeBatch: (queries: any[]) => Promise<T[]> ): Promise<void> { const batch = this.batchQueue.get(batchKey); if (!batch || batch.length === 0) return; // Clear batch queue and timer this.batchQueue.delete(batchKey); if (this.batchTimers.has(batchKey)) { clearTimeout(this.batchTimers.get(batchKey)!); this.batchTimers.delete(batchKey); } try { const queries = batch.map(item => item.query); const results = await executeBatch(queries); // Resolve individual promises batch.forEach((item, index) => { if (results[index] !== undefined) { item.resolve(results[index]); } else { item.reject(new Error('No result for batch query')); } }); this.emit('batch:executed', { batchKey, size: batch.length }); } catch (error) { // Reject all promises in batch batch.forEach(item => item.reject(error)); this.emit('batch:error', { batchKey, error }); } } /** * Optimize a dependency query for better performance */ optimizeDependencyQuery(query: { target: string; depth?: number; includeTests?: boolean; }): { optimized: boolean; query: any; strategy: string; } { if (!this.config.enableQueryOptimization) { return { optimized: false, query, strategy: 'none' }; } let strategy = 'standard'; let optimizedQuery = { ...query }; // Limit depth for performance if (!query.depth || query.depth > 3) { optimizedQuery.depth = 3; strategy = 'depth-limited'; } // Skip tests by default for better performance if (query.includeTests === undefined) { optimizedQuery.includeTests = false; strategy = strategy === 'standard' ? 'skip-tests' : `${strategy}+skip-tests`; } return { optimized: strategy !== 'standard', query: optimizedQuery, strategy }; } /** * Create a cache key from query parameters */ createCacheKey(operation: string, params: any): string { const hash = createHash('sha256'); hash.update(operation); hash.update(JSON.stringify(params)); return hash.digest('hex'); } /** * Prefetch data that is likely to be needed */ async prefetch<T>( predictions: Array<{ queryFn: () => Promise<T>; cacheKey: string; probability: number; }> ): Promise<void> { // Sort by probability and prefetch top candidates const toPrefetch = predictions .filter(p => p.probability > 0.5) .sort((a, b) => b.probability - a.probability) .slice(0, 5); await Promise.all( toPrefetch.map(prediction => this.executeQuery( prediction.queryFn, prediction.cacheKey, { priority: -1 } // Low priority for prefetch ).catch(() => { // Ignore prefetch errors }) ) ); this.emit('prefetch:complete', { count: toPrefetch.length }); } /** * Compress large results for better memory usage */ compressResult(data: any): any { if (!this.config.enableResultCompression) { return data; } const dataStr = JSON.stringify(data); if (dataStr.length < 10000) { return data; // Don't compress small data } // Simple compression: remove unnecessary whitespace and duplicates // In production, you might use a proper compression library return { compressed: true, data: JSON.parse(dataStr) // This would be actual compression }; } /** * Decompress compressed results */ decompressResult(data: any): any { if (!data.compressed) { return data; } return data.data; // This would be actual decompression } /** * Record response time for metrics */ private recordResponseTime(time: number): void { this.responseTimes.push(time); // Keep only last 1000 measurements if (this.responseTimes.length > 1000) { this.responseTimes.shift(); } // Update metrics this.updateMetrics(); } /** * Update performance metrics */ private updateMetrics(): void { if (this.responseTimes.length === 0) return; const sorted = [...this.responseTimes].sort((a, b) => a - b); this.metrics.avgResponseTime = sorted.reduce((a, b) => a + b, 0) / sorted.length; this.metrics.p95ResponseTime = sorted[Math.floor(sorted.length * 0.95)]; this.metrics.p99ResponseTime = sorted[Math.floor(sorted.length * 0.99)]; } /** * Get current performance metrics */ getMetrics(): QueryMetrics { return { ...this.metrics }; } /** * Clear all caches and reset metrics */ reset(): void { this.cache.clear(); this.batchQueue.clear(); this.batchTimers.forEach(timer => clearTimeout(timer)); this.batchTimers.clear(); this.responseTimes = []; this.metrics = { hits: 0, misses: 0, evictions: 0, avgResponseTime: 0, p95ResponseTime: 0, p99ResponseTime: 0 }; this.emit('optimizer:reset'); } /** * Get cache statistics */ getCacheStats(): { size: number; maxSize: number; hitRate: number; missRate: number; } { const total = this.metrics.hits + this.metrics.misses; return { size: this.cache.size, maxSize: this.config.cacheSize, hitRate: total > 0 ? this.metrics.hits / total : 0, missRate: total > 0 ? this.metrics.misses / total : 0 }; } /** * Get queue statistics */ getQueueStats(): { size: number; pending: number; isPaused: boolean; } { return { size: this.queue.size, pending: this.queue.pending, isPaused: this.queue.isPaused }; } /** * Pause query processing */ pause(): void { this.queue.pause(); this.emit('optimizer:paused'); } /** * Resume query processing */ resume(): void { this.queue.start(); this.emit('optimizer:resumed'); } } /** * Query optimization strategies */ export class QueryOptimizationStrategy { /** * Optimize file pattern queries */ static optimizeFilePattern(pattern: string): string[] { // Split broad patterns into more specific ones if (pattern === '**/*') { return ['**/*.ts', '**/*.js', '**/*.tsx', '**/*.jsx']; } if (pattern === 'src/**/*') { return ['src/**/*.ts', 'src/**/*.js', 'src/**/*.tsx', 'src/**/*.jsx']; } return [pattern]; } /** * Optimize dependency depth based on target type */ static optimizeDependencyDepth(target: string, requestedDepth?: number): number { // File extensions const ext = target.split('.').pop(); // Test files usually have fewer dependencies if (target.includes('.test.') || target.includes('.spec.')) { return Math.min(requestedDepth ?? 2, 2); } // Config files typically have shallow dependencies if (target.includes('config') || ext === 'json' || ext === 'yaml') { return Math.min(requestedDepth ?? 1, 1); } // Entry points might need deeper analysis if (target.includes('index') || target.includes('main') || target.includes('app')) { return requestedDepth ?? 4; } // Default depth return requestedDepth ?? 3; } /** * Determine if parallel execution would be beneficial */ static shouldParallelize(queries: any[]): boolean { // Parallelize if we have multiple queries if (queries.length < 2) return false; // Don't parallelize if queries are interdependent const targets = queries.map(q => q.target).filter(Boolean); const uniqueTargets = new Set(targets); // If targets overlap significantly, sequential might be better if (uniqueTargets.size < targets.length * 0.7) { return false; } return true; } /** * Group queries for efficient batching */ static groupQueries(queries: any[]): Map<string, any[]> { const groups = new Map<string, any[]>(); for (const query of queries) { const key = `${query.type}-${query.depth ?? 'default'}`; if (!groups.has(key)) { groups.set(key, []); } groups.get(key)!.push(query); } return groups; } }

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/Jakedismo/codegraph-rust'

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