UNPKG

mira-consciousness

Version:

Memory & Intelligence Retention Archive - Preserving The Spark

633 lines 26.4 kB
/** * Caching Strategy Analyzer * Analyzes caching implementations and identifies optimization opportunities */ import fs from 'fs-extra'; import * as path from 'path'; import { glob } from 'glob'; export class CachingAnalyzer { projectRoot; constructor(projectRoot) { this.projectRoot = projectRoot; } async analyze(issues) { const implementedCaches = await this.detectImplementedCaches(); const missedOpportunities = await this.identifyMissedCachingOpportunities(); const cachingStrategy = await this.analyzeCachingStrategyType(); const cacheEfficiency = this.calculateCacheEfficiency(implementedCaches); const recommendations = this.generateCachingRecommendations(implementedCaches, missedOpportunities, cacheEfficiency); // Calculate overall caching score const overallCachingScore = this.calculateCachingScore(implementedCaches, missedOpportunities, cacheEfficiency); // Determine risk level let riskLevel; if (overallCachingScore >= 85) riskLevel = 'optimal'; else if (overallCachingScore >= 70) riskLevel = 'good'; else if (overallCachingScore >= 50) riskLevel = 'needs_improvement'; else riskLevel = 'poor'; // Add caching-related performance issues this.addCachingIssues(issues, implementedCaches, missedOpportunities, cacheEfficiency); return { cachingStrategy, implementedCaches, missedOpportunities, cacheEfficiency, recommendations, overallCachingScore, riskLevel }; } async detectImplementedCaches() { const caches = []; try { const files = await glob('**/*.{ts,js,jsx,tsx,py}', { cwd: this.projectRoot, ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**'] }); for (const file of files) { const filePath = path.join(this.projectRoot, file); const content = await fs.readFile(filePath, 'utf-8'); // Detect various caching patterns await this.detectMemoryCache(content, file, caches); await this.detectFileSystemCache(content, file, caches); await this.detectRedisCache(content, file, caches); await this.detectDatabaseCache(content, file, caches); await this.detectHttpCache(content, file, caches); await this.detectComputationCache(content, file, caches); } // Check for package.json caching dependencies await this.detectCachingLibraries(caches); } catch (error) { // Continue with what we have } return caches; } async detectMemoryCache(content, file, caches) { const patterns = [ { pattern: /new\s+Map\s*\(/, name: 'JavaScript Map cache' }, { pattern: /new\s+WeakMap\s*\(/, name: 'JavaScript WeakMap cache' }, { pattern: /\.cache\s*=/, name: 'Property-based cache' }, { pattern: /memoize\s*\(/, name: 'Memoization cache' }, { pattern: /lru-cache|node-cache/, name: 'LRU Cache library' }, { pattern: /memory-cache/, name: 'Memory cache library' }, { pattern: /Map\(\)\s*\/\/.*cache/i, name: 'Documented Map cache' } ]; for (const { pattern, name } of patterns) { if (pattern.test(content)) { caches.push({ name, type: 'memory', location: file, estimatedSize: this.estimateCacheSize(content, pattern), usage: this.analyzeCacheUsage(content, pattern), effectiveness: this.estimateCacheEffectiveness(content, pattern) }); } } } async detectFileSystemCache(content, file, caches) { const patterns = [ { pattern: /\.cache\//i, name: 'Cache directory' }, { pattern: /temp.*cache|cache.*temp/i, name: 'Temporary file cache' }, { pattern: /fs\.writeFile.*cache|fs\.readFile.*cache/i, name: 'File-based cache' }, { pattern: /\.json.*cache|cache.*\.json/i, name: 'JSON cache files' }, { pattern: /disk.*cache|cache.*disk/i, name: 'Disk cache' } ]; for (const { pattern, name } of patterns) { if (pattern.test(content)) { caches.push({ name, type: 'disk', location: file, estimatedSize: this.estimateCacheSize(content, pattern), usage: this.analyzeCacheUsage(content, pattern), effectiveness: this.estimateCacheEffectiveness(content, pattern) }); } } } async detectRedisCache(content, file, caches) { const patterns = [ { pattern: /redis\.get|redis\.set/, name: 'Redis cache operations' }, { pattern: /createClient.*redis/i, name: 'Redis client cache' }, { pattern: /ioredis/, name: 'IORedis cache' }, { pattern: /node_redis/, name: 'Node Redis cache' } ]; for (const { pattern, name } of patterns) { if (pattern.test(content)) { caches.push({ name, type: 'network', location: file, estimatedSize: 0, // External usage: this.analyzeCacheUsage(content, pattern), effectiveness: this.estimateCacheEffectiveness(content, pattern) }); } } } async detectDatabaseCache(content, file, caches) { const patterns = [ { pattern: /query.*cache|cache.*query/i, name: 'Database query cache' }, { pattern: /result.*cache|cache.*result/i, name: 'Database result cache' }, { pattern: /connection.*pool/i, name: 'Database connection pool' }, { pattern: /prepared.*statement/i, name: 'Prepared statement cache' } ]; for (const { pattern, name } of patterns) { if (pattern.test(content)) { caches.push({ name, type: 'memory', location: file, estimatedSize: this.estimateCacheSize(content, pattern), usage: this.analyzeCacheUsage(content, pattern), effectiveness: this.estimateCacheEffectiveness(content, pattern) }); } } } async detectHttpCache(content, file, caches) { const patterns = [ { pattern: /cache-control|etag|last-modified/i, name: 'HTTP cache headers' }, { pattern: /axios.*cache|fetch.*cache/i, name: 'HTTP request cache' }, { pattern: /service.*worker/i, name: 'Service Worker cache' }, { pattern: /sw\.cache|cache\.add/i, name: 'Cache API' } ]; for (const { pattern, name } of patterns) { if (pattern.test(content)) { caches.push({ name, type: 'network', location: file, estimatedSize: 0, // External usage: this.analyzeCacheUsage(content, pattern), effectiveness: this.estimateCacheEffectiveness(content, pattern) }); } } } async detectComputationCache(content, file, caches) { const patterns = [ { pattern: /useMemo|useCallback/i, name: 'React memoization' }, { pattern: /@memoize|@cache/i, name: 'Decorator-based cache' }, { pattern: /computed.*cache|cache.*computed/i, name: 'Computed value cache' }, { pattern: /expensive.*cache|cache.*expensive/i, name: 'Expensive operation cache' } ]; for (const { pattern, name } of patterns) { if (pattern.test(content)) { caches.push({ name, type: 'computed', location: file, estimatedSize: this.estimateCacheSize(content, pattern), usage: this.analyzeCacheUsage(content, pattern), effectiveness: this.estimateCacheEffectiveness(content, pattern) }); } } } async detectCachingLibraries(caches) { const packageJsonPath = path.join(this.projectRoot, 'package.json'); if (await fs.pathExists(packageJsonPath)) { try { const packageJson = await fs.readJson(packageJsonPath); const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies }; const cachingLibraries = [ { name: 'lru-cache', type: 'memory' }, { name: 'node-cache', type: 'memory' }, { name: 'memory-cache', type: 'memory' }, { name: 'redis', type: 'network' }, { name: 'ioredis', type: 'network' }, { name: 'memcached', type: 'network' }, { name: 'memoizee', type: 'computed' }, { name: 'lodash.memoize', type: 'computed' } ]; for (const lib of cachingLibraries) { if (dependencies[lib.name]) { caches.push({ name: `${lib.name} library`, type: lib.type, location: 'package.json', estimatedSize: 0, usage: 'unknown', effectiveness: 'unknown' }); } } } catch (error) { // Skip if can't read package.json } } } async identifyMissedCachingOpportunities() { const opportunities = []; try { const files = await glob('**/*.{ts,js,jsx,tsx}', { cwd: this.projectRoot, ignore: ['node_modules/**', '.git/**', 'dist/**', '**/*.test.*', '**/*.spec.*'] }); for (const file of files) { const filePath = path.join(this.projectRoot, file); const content = await fs.readFile(filePath, 'utf-8'); // Check for expensive computations this.detectExpensiveComputations(content, file, opportunities); // Check for repeated database queries this.detectRepeatedDatabaseQueries(content, file, opportunities); // Check for repeated API calls this.detectRepeatedApiCalls(content, file, opportunities); // Check for repeated file I/O this.detectRepeatedFileIO(content, file, opportunities); // Check for repeated calculations this.detectRepeatedCalculations(content, file, opportunities); } } catch (error) { // Continue with what we have } return opportunities; } detectExpensiveComputations(content, file, opportunities) { const patterns = [ { pattern: /for\s*\([^)]*\)\s*{[^}]*for\s*\([^)]*\)\s*{/, type: 'expensive_computation', message: 'Nested loops detected - consider caching results' }, { pattern: /\.sort\s*\([^)]*\).*\.filter\s*\([^)]*\).*\.map\s*\(/, type: 'expensive_computation', message: 'Complex array operations chain - consider memoization' }, { pattern: /JSON\.parse\s*\([^)]*JSON\.stringify/, type: 'expensive_computation', message: 'JSON parse/stringify operations - cache parsed results' } ]; patterns.forEach(({ pattern, type, message }) => { if (pattern.test(content)) { opportunities.push({ type, location: file, estimatedCost: 100, // milliseconds frequency: 'high', cachability: 'excellent', recommendation: message, potentialSavings: 80 }); } }); } detectRepeatedDatabaseQueries(content, file, opportunities) { const queryPatterns = [ /\.query\s*\(/g, /\.find\s*\(/g, /\.findOne\s*\(/g, /\.aggregate\s*\(/g ]; queryPatterns.forEach(pattern => { const matches = content.match(pattern); if (matches && matches.length > 3) { opportunities.push({ type: 'database_query', location: file, estimatedCost: 50, frequency: 'very_high', cachability: 'good', recommendation: 'Multiple database queries detected - implement query result caching', potentialSavings: 70 }); } }); } detectRepeatedApiCalls(content, file, opportunities) { const apiPatterns = [ /fetch\s*\(/g, /axios\./g, /\.get\s*\(/g, /\.post\s*\(/g ]; apiPatterns.forEach(pattern => { const matches = content.match(pattern); if (matches && matches.length > 2) { opportunities.push({ type: 'api_call', location: file, estimatedCost: 200, frequency: 'medium', cachability: 'moderate', recommendation: 'Multiple API calls detected - consider response caching', potentialSavings: 60 }); } }); } detectRepeatedFileIO(content, file, opportunities) { const filePatterns = [ /fs\.readFile/g, /fs\.readFileSync/g, /fs\.read/g ]; filePatterns.forEach(pattern => { const matches = content.match(pattern); if (matches && matches.length > 2) { opportunities.push({ type: 'file_io', location: file, estimatedCost: 30, frequency: 'high', cachability: 'excellent', recommendation: 'Multiple file reads detected - cache file contents', potentialSavings: 90 }); } }); } detectRepeatedCalculations(content, file, opportunities) { // Look for functions that might be called repeatedly const functionCalls = content.match(/(\w+)\s*\(/g) || []; const callCounts = new Map(); functionCalls.forEach(call => { const funcName = call.replace(/\s*\(/, ''); callCounts.set(funcName, (callCounts.get(funcName) || 0) + 1); }); // Find functions called many times for (const [funcName, count] of callCounts) { if (count > 5 && !['if', 'for', 'while', 'console', 'log'].includes(funcName)) { opportunities.push({ type: 'expensive_computation', location: `${file} - ${funcName}()`, estimatedCost: 20, frequency: 'very_high', cachability: 'good', recommendation: `Function ${funcName}() called ${count} times - consider memoization`, potentialSavings: 50 }); } } } async analyzeCachingStrategyType() { const implementedCaches = await this.detectImplementedCaches(); // Determine cache types in use const cacheTypes = new Set(implementedCaches.map(c => c.type)); const hasMemoryCache = cacheTypes.has('memory'); const hasNetworkCache = cacheTypes.has('network'); const hasDiskCache = cacheTypes.has('disk'); // Determine strategy type let type = 'none'; if (hasMemoryCache && hasNetworkCache) { type = 'hybrid'; } else if (hasNetworkCache && implementedCaches.some(c => c.name.includes('Redis'))) { type = 'redis'; } else if (hasDiskCache) { type = 'filesystem'; } else if (hasMemoryCache) { type = 'in-memory'; } // Analyze cache layers const layers = []; if (hasMemoryCache) layers.push('memory'); if (hasDiskCache) layers.push('disk'); if (hasNetworkCache) layers.push('network'); // Determine TTL strategy let ttlStrategy = 'none'; const hasTTL = implementedCaches.some(c => c.ttl !== undefined); if (hasTTL) ttlStrategy = 'fixed'; // Determine invalidation strategy let invalidationStrategy = 'none'; if (implementedCaches.some(c => c.name.includes('event'))) { invalidationStrategy = 'event-based'; } else if (hasTTL) { invalidationStrategy = 'time-based'; } return { type, layers, ttlStrategy, invalidationStrategy, consistency: layers.length > 1 ? 'eventual' : 'strong' }; } calculateCacheEfficiency(caches) { const totalCaches = caches.length; const activeCaches = caches.filter(c => c.usage !== 'rare').length; const redundantCaches = this.findRedundantCaches(caches); const expiredCaches = caches.filter(c => c.effectiveness === 'low').length; // Calculate average hit ratio (simulated) const averageHitRatio = caches .filter(c => c.hitRatio !== undefined) .reduce((sum, c) => sum + (c.hitRatio || 0), 0) / totalCaches || 0.7; // Calculate memory utilization const totalMemory = caches.reduce((sum, c) => sum + c.estimatedSize, 0); const effectiveMemory = caches .filter(c => c.effectiveness === 'high') .reduce((sum, c) => sum + c.estimatedSize, 0); const memoryUtilization = totalMemory > 0 ? effectiveMemory / totalMemory : 0; // Calculate optimization potential const optimizationPotential = redundantCaches + expiredCaches; return { totalCaches, activeCaches, averageHitRatio, memoryUtilization, redundantCaches, expiredCaches, optimizationPotential }; } findRedundantCaches(caches) { const cacheGroups = new Map(); // Group caches by type and location caches.forEach(cache => { const key = `${cache.type}-${cache.location}`; if (!cacheGroups.has(key)) { cacheGroups.set(key, []); } cacheGroups.get(key).push(cache); }); // Count groups with multiple caches let redundant = 0; for (const group of cacheGroups.values()) { if (group.length > 1) { redundant += group.length - 1; } } return redundant; } generateCachingRecommendations(caches, opportunities, efficiency) { const recommendations = []; // Check for no caching if (caches.length === 0) { recommendations.push({ priority: 'critical', category: 'implementation', title: 'Implement basic caching strategy', description: 'No caching detected. Start with in-memory caching for frequently accessed data.', expectedImpact: 'major', effort: 'medium', implementation: [ 'Install an LRU cache library (e.g., lru-cache)', 'Identify hot paths in your application', 'Implement caching for expensive operations', 'Add cache metrics and monitoring' ] }); } // Check for missed opportunities if (opportunities.length > 5) { recommendations.push({ priority: 'high', category: 'optimization', title: 'Cache expensive operations', description: `Found ${opportunities.length} caching opportunities that could improve performance.`, expectedImpact: 'major', effort: 'low', implementation: opportunities.slice(0, 3).map(o => o.recommendation) }); } // Check for inefficient caching if (efficiency.averageHitRatio < 0.5) { recommendations.push({ priority: 'high', category: 'optimization', title: 'Improve cache hit ratio', description: 'Low cache hit ratio detected. Review cache keys and TTL values.', expectedImpact: 'moderate', effort: 'low', implementation: [ 'Analyze cache key patterns', 'Increase TTL for stable data', 'Implement cache warming strategies', 'Consider predictive caching' ] }); } // Check for redundant caches if (efficiency.redundantCaches > 0) { recommendations.push({ priority: 'medium', category: 'cleanup', title: 'Remove redundant caches', description: `Found ${efficiency.redundantCaches} redundant cache implementations.`, expectedImpact: 'minor', effort: 'low', implementation: [ 'Consolidate similar caches', 'Create a centralized cache manager', 'Remove duplicate cache logic' ] }); } // Architecture recommendations if (caches.length > 10 && !caches.some(c => c.name.includes('manager'))) { recommendations.push({ priority: 'medium', category: 'architecture', title: 'Implement cache management layer', description: 'Multiple caches detected without central management.', expectedImpact: 'moderate', effort: 'high', implementation: [ 'Create a cache manager service', 'Implement cache statistics collection', 'Add cache invalidation strategies', 'Create cache configuration system' ] }); } return recommendations; } calculateCachingScore(caches, opportunities, efficiency) { let score = 50; // Base score // Points for having caching if (caches.length > 0) score += 20; // Points for cache efficiency score += efficiency.averageHitRatio * 20; // Points for active caches const activeRatio = efficiency.totalCaches > 0 ? efficiency.activeCaches / efficiency.totalCaches : 0; score += activeRatio * 10; // Deduct for missed opportunities score -= Math.min(20, opportunities.length * 2); // Deduct for redundant caches score -= efficiency.redundantCaches * 3; // Deduct for expired caches score -= efficiency.expiredCaches * 2; return Math.max(0, Math.min(100, Math.round(score))); } addCachingIssues(issues, caches, opportunities, efficiency) { // Add issue for no caching if (caches.length === 0) { issues.push({ file: 'project', type: 'no_caching', message: 'No caching strategy detected', impact: 'high', recommendation: 'Implement caching for frequently accessed data' }); } // Add issue for many missed opportunities if (opportunities.length > 10) { issues.push({ file: 'project', type: 'missed_caching_opportunities', message: `${opportunities.length} caching opportunities identified`, impact: 'medium', recommendation: 'Review and implement caching for expensive operations' }); } // Add issue for poor cache efficiency if (efficiency.averageHitRatio < 0.3) { issues.push({ file: 'cache-system', type: 'poor_cache_efficiency', message: `Cache hit ratio is very low (${(efficiency.averageHitRatio * 100).toFixed(0)}%)`, impact: 'high', recommendation: 'Review cache invalidation and key strategies' }); } } estimateCacheSize(content, pattern) { // Simple heuristic based on code patterns const matches = content.match(pattern) || []; return matches.length * 1024; // 1KB per match as estimate } analyzeCacheUsage(content, pattern) { const matches = content.match(pattern) || []; if (matches.length > 10) return 'frequent'; if (matches.length > 5) return 'moderate'; if (matches.length > 0) return 'rare'; return 'unknown'; } estimateCacheEffectiveness(content, pattern) { // Look for cache hit/miss patterns if (content.includes('cache.hit') || content.includes('cacheHit')) { return 'high'; } if (content.includes('cache.get') || content.includes('cache.set')) { return 'medium'; } return 'unknown'; } } //# sourceMappingURL=CachingAnalyzer.js.map