Skip to main content
Glama
sascodiego

MCP Vibe Coding Knowledge Graph

by sascodiego
CacheWarmer.js40.5 kB
/** * CONTEXT: Intelligent cache warming and preloading system for MCP server optimization * REASON: Proactively load frequently accessed data to reduce cache misses and improve performance * CHANGE: Smart cache warming strategies based on usage patterns and predictive analytics * PREVENTION: Cache misses, slow response times, inefficient resource utilization */ import { EventEmitter } from 'events'; import { logger } from '../utils/logger.js'; import fs from 'fs/promises'; import path from 'path'; export class CacheWarmer extends EventEmitter { constructor(config = {}) { super(); this.config = { // Warming strategies strategies: { predictiveWarming: config.strategies?.predictiveWarming !== false, patternBasedWarming: config.strategies?.patternBasedWarming !== false, timeBasedWarming: config.strategies?.timeBasedWarming !== false, dependencyWarming: config.strategies?.dependencyWarming !== false, adaptiveWarming: config.strategies?.adaptiveWarming !== false, ...config.strategies }, // Warming intervals and timing warmingInterval: config.warmingInterval || 300000, // 5 minutes preloadInterval: config.preloadInterval || 60000, // 1 minute analysisInterval: config.analysisInterval || 120000, // 2 minutes // Pattern analysis settings patternAnalysis: { minRequests: config.patternAnalysis?.minRequests || 5, minFrequency: config.patternAnalysis?.minFrequency || 0.1, // 10% timeWindow: config.patternAnalysis?.timeWindow || 3600000, // 1 hour maxPatterns: config.patternAnalysis?.maxPatterns || 1000, ...config.patternAnalysis }, // Predictive settings predictive: { enabled: config.predictive?.enabled !== false, lookAheadTime: config.predictive?.lookAheadTime || 1800000, // 30 minutes confidenceThreshold: config.predictive?.confidenceThreshold || 0.7, maxPredictions: config.predictive?.maxPredictions || 100, ...config.predictive }, // Resource limits resources: { maxWarmingConcurrency: config.resources?.maxWarmingConcurrency || 5, maxMemoryUsage: config.resources?.maxMemoryUsage || 100 * 1024 * 1024, // 100MB maxWarmingTime: config.resources?.maxWarmingTime || 30000, // 30 seconds ...config.resources }, // Priority settings priority: { highPriorityThreshold: config.priority?.highPriorityThreshold || 0.8, mediumPriorityThreshold: config.priority?.mediumPriorityThreshold || 0.5, lowPriorityThreshold: config.priority?.lowPriorityThreshold || 0.2, ...config.priority } }; // Cache registries this.cacheProviders = new Map(); this.warmingQueues = new Map(['high', 'medium', 'low'].map(p => [p, []])); // Pattern tracking this.accessPatterns = new Map(); this.requestHistory = []; this.timeBasedPatterns = new Map(); this.dependencyGraph = new Map(); // Predictive analytics this.predictor = new CachePredictionEngine(this); this.patternAnalyzer = new AccessPatternAnalyzer(this); this.dependencyTracker = new DependencyTracker(this); // Warming state this.warmingState = { active: false, inProgress: new Set(), completed: new Set(), failed: new Set(), lastWarming: null, currentPriority: 'medium' }; // Performance metrics this.metrics = { warming: { total: 0, successful: 0, failed: 0, averageTime: 0, totalTime: 0, byPriority: new Map(), byStrategy: new Map() }, patterns: { detected: 0, predicted: 0, accuracy: 0, coverage: 0 }, cache: { preloadedItems: 0, hitRateImprovement: 0, missReduction: 0, responseTtimeImprovement: 0 }, resources: { memoryUsed: 0, cpuTime: 0, concurrentWarming: 0 } }; // Monitoring intervals this.monitoringIntervals = []; this.initialized = false; } /** * Initialize cache warmer */ async initialize() { if (this.initialized) return; try { logger.info('Initializing cache warmer', { strategies: this.config.strategies, intervals: { warming: this.config.warmingInterval, preload: this.config.preloadInterval, analysis: this.config.analysisInterval } }); // Initialize sub-systems await this.predictor.initialize(); await this.patternAnalyzer.initialize(); await this.dependencyTracker.initialize(); // Start monitoring and warming processes this.startMonitoring(); this.initialized = true; this.emit('initialized'); logger.info('Cache warmer initialized successfully'); } catch (error) { logger.error('Failed to initialize cache warmer:', error); throw error; } } /** * Register cache provider for warming */ registerCacheProvider(name, provider, config = {}) { const providerConfig = { name: name, provider: provider, warmingEnabled: config.warmingEnabled !== false, preloadEnabled: config.preloadEnabled !== false, priority: config.priority || 'medium', warmingMethods: config.warmingMethods || ['get', 'preload'], dependencies: config.dependencies || [], keyPatterns: config.keyPatterns || [], ...config }; this.cacheProviders.set(name, providerConfig); // Setup event listeners for cache provider if (provider.on) { provider.on('get', (event) => this.trackCacheAccess(name, event)); provider.on('miss', (event) => this.trackCacheMiss(name, event)); provider.on('set', (event) => this.trackCacheSet(name, event)); } logger.debug(`Cache provider registered: ${name}`, providerConfig); this.emit('providerRegistered', { name, config: providerConfig }); } /** * Unregister cache provider */ unregisterCacheProvider(name) { if (this.cacheProviders.has(name)) { this.cacheProviders.delete(name); logger.debug(`Cache provider unregistered: ${name}`); this.emit('providerUnregistered', { name }); } } /** * Start monitoring and warming processes */ startMonitoring() { // Pattern analysis const patternAnalysisInterval = setInterval(() => { this.patternAnalyzer.analyzePatterns().catch(error => { logger.error('Pattern analysis failed:', error); }); }, this.config.analysisInterval); // Predictive warming const predictiveWarmingInterval = setInterval(() => { if (this.config.strategies.predictiveWarming) { this.runPredictiveWarming().catch(error => { logger.error('Predictive warming failed:', error); }); } }, this.config.warmingInterval); // Time-based warming const timeBasedWarmingInterval = setInterval(() => { if (this.config.strategies.timeBasedWarming) { this.runTimeBasedWarming().catch(error => { logger.error('Time-based warming failed:', error); }); } }, this.config.preloadInterval); // Pattern-based warming const patternBasedWarmingInterval = setInterval(() => { if (this.config.strategies.patternBasedWarming) { this.runPatternBasedWarming().catch(error => { logger.error('Pattern-based warming failed:', error); }); } }, this.config.warmingInterval); // Dependency warming const dependencyWarmingInterval = setInterval(() => { if (this.config.strategies.dependencyWarming) { this.runDependencyWarming().catch(error => { logger.error('Dependency warming failed:', error); }); } }, this.config.warmingInterval); // Queue processing const queueProcessingInterval = setInterval(() => { this.processWarmingQueues().catch(error => { logger.error('Queue processing failed:', error); }); }, 5000); // Process every 5 seconds this.monitoringIntervals.push( patternAnalysisInterval, predictiveWarmingInterval, timeBasedWarmingInterval, patternBasedWarmingInterval, dependencyWarmingInterval, queueProcessingInterval ); logger.debug('Cache warming monitoring started'); } /** * Track cache access for pattern analysis */ trackCacheAccess(providerName, event) { const access = { provider: providerName, key: event.key, timestamp: Date.now(), hit: event.hit || false, source: event.source || 'unknown', metadata: event.metadata || {} }; // Add to request history this.requestHistory.push(access); // Limit history size if (this.requestHistory.length > 10000) { this.requestHistory.shift(); } // Update access patterns this.updateAccessPatterns(access); // Track dependencies this.dependencyTracker.trackAccess(access); this.emit('cacheAccess', access); } /** * Track cache miss for warming prioritization */ trackCacheMiss(providerName, event) { const miss = { provider: providerName, key: event.key, timestamp: Date.now(), reason: event.reason || 'not_found', cost: event.cost || 1, metadata: event.metadata || {} }; // Prioritize warming for missed keys this.prioritizeWarming(miss); this.emit('cacheMiss', miss); } /** * Track cache set operations */ trackCacheSet(providerName, event) { const set = { provider: providerName, key: event.key, timestamp: Date.now(), size: event.size || 0, ttl: event.ttl || 0, metadata: event.metadata || {} }; this.emit('cacheSet', set); } /** * Update access patterns */ updateAccessPatterns(access) { const key = access.key; const provider = access.provider; const patternKey = `${provider}:${key}`; if (!this.accessPatterns.has(patternKey)) { this.accessPatterns.set(patternKey, { key: key, provider: provider, count: 0, firstAccess: access.timestamp, lastAccess: access.timestamp, frequency: 0, timePattern: [], hitRate: 0, hits: 0, totalRequests: 0 }); } const pattern = this.accessPatterns.get(patternKey); pattern.count++; pattern.lastAccess = access.timestamp; pattern.timePattern.push(access.timestamp); pattern.totalRequests++; if (access.hit) { pattern.hits++; } pattern.hitRate = pattern.hits / pattern.totalRequests; // Calculate frequency (requests per hour) const timeSpan = pattern.lastAccess - pattern.firstAccess; pattern.frequency = timeSpan > 0 ? (pattern.count / timeSpan) * 3600000 : 0; // Keep only recent time pattern data if (pattern.timePattern.length > 100) { pattern.timePattern.shift(); } } /** * Prioritize warming based on cache miss */ prioritizeWarming(miss) { // Calculate priority based on miss frequency and cost const patternKey = `${miss.provider}:${miss.key}`; const pattern = this.accessPatterns.get(patternKey); let priority = 'low'; if (pattern) { const missRate = 1 - pattern.hitRate; const frequency = pattern.frequency; const cost = miss.cost; const score = (missRate * 0.4) + (frequency * 0.4) + (cost * 0.2); if (score > this.config.priority.highPriorityThreshold) { priority = 'high'; } else if (score > this.config.priority.mediumPriorityThreshold) { priority = 'medium'; } } // Add to warming queue this.addToWarmingQueue(miss.provider, miss.key, priority, { reason: 'cache_miss', missData: miss }); } /** * Add item to warming queue */ addToWarmingQueue(provider, key, priority = 'medium', metadata = {}) { const warmingItem = { provider: provider, key: key, priority: priority, timestamp: Date.now(), attempts: 0, metadata: metadata }; const queue = this.warmingQueues.get(priority); if (queue) { // Check if item is already in queue const exists = queue.some(item => item.provider === provider && item.key === key ); if (!exists) { queue.push(warmingItem); this.emit('warmingQueued', warmingItem); } } } /** * Process warming queues */ async processWarmingQueues() { if (this.warmingState.active) return; const maxConcurrency = this.config.resources.maxWarmingConcurrency; if (this.warmingState.inProgress.size >= maxConcurrency) return; // Process high priority queue first for (const priority of ['high', 'medium', 'low']) { const queue = this.warmingQueues.get(priority); while (queue.length > 0 && this.warmingState.inProgress.size < maxConcurrency) { const item = queue.shift(); await this.warmCacheItem(item); } } } /** * Warm individual cache item */ async warmCacheItem(item) { const warmingId = `${item.provider}:${item.key}:${Date.now()}`; try { this.warmingState.inProgress.add(warmingId); this.metrics.resources.concurrentWarming = this.warmingState.inProgress.size; const startTime = Date.now(); const provider = this.cacheProviders.get(item.provider); if (!provider || !provider.warmingEnabled) { throw new Error(`Provider ${item.provider} not available or warming disabled`); } // Perform warming operation await this.performWarming(provider, item.key, item.metadata); const duration = Date.now() - startTime; // Update metrics this.updateWarmingMetrics(item, duration, true); this.warmingState.completed.add(warmingId); this.emit('warmingCompleted', { ...item, duration: duration, success: true }); } catch (error) { const duration = Date.now() - (this.warmingState.startTime || Date.now()); // Update metrics this.updateWarmingMetrics(item, duration, false); this.warmingState.failed.add(warmingId); logger.error('Cache warming failed:', { provider: item.provider, key: item.key, error: error.message }); this.emit('warmingFailed', { ...item, duration: duration, error: error, success: false }); // Retry with lower priority if not at max attempts if (item.attempts < 3) { item.attempts++; const lowerPriority = item.priority === 'high' ? 'medium' : 'low'; setTimeout(() => { this.addToWarmingQueue(item.provider, item.key, lowerPriority, item.metadata); }, 30000 * item.attempts); // Exponential backoff } } finally { this.warmingState.inProgress.delete(warmingId); this.metrics.resources.concurrentWarming = this.warmingState.inProgress.size; } } /** * Perform actual warming operation */ async performWarming(provider, key, metadata = {}) { const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error('Warming timeout')), this.config.resources.maxWarmingTime); }); const warmingPromise = (async () => { // Try different warming methods if (provider.provider.warm) { return await provider.provider.warm(key, metadata); } if (provider.provider.preload) { return await provider.provider.preload(key, metadata); } if (provider.provider.get) { return await provider.provider.get(key, { ...metadata, warming: true }); } throw new Error('No warming method available'); })(); return await Promise.race([warmingPromise, timeoutPromise]); } /** * Update warming metrics */ updateWarmingMetrics(item, duration, success) { this.metrics.warming.total++; this.metrics.warming.totalTime += duration; this.metrics.warming.averageTime = this.metrics.warming.totalTime / this.metrics.warming.total; if (success) { this.metrics.warming.successful++; } else { this.metrics.warming.failed++; } // Update priority metrics if (!this.metrics.warming.byPriority.has(item.priority)) { this.metrics.warming.byPriority.set(item.priority, { total: 0, successful: 0, failed: 0, averageTime: 0, totalTime: 0 }); } const priorityStats = this.metrics.warming.byPriority.get(item.priority); priorityStats.total++; priorityStats.totalTime += duration; priorityStats.averageTime = priorityStats.totalTime / priorityStats.total; if (success) { priorityStats.successful++; } else { priorityStats.failed++; } // Update strategy metrics const strategy = item.metadata.reason || 'unknown'; if (!this.metrics.warming.byStrategy.has(strategy)) { this.metrics.warming.byStrategy.set(strategy, { total: 0, successful: 0, failed: 0 }); } const strategyStats = this.metrics.warming.byStrategy.get(strategy); strategyStats.total++; if (success) { strategyStats.successful++; } else { strategyStats.failed++; } } /** * Run predictive warming */ async runPredictiveWarming() { if (!this.config.predictive.enabled) return; try { const predictions = await this.predictor.generatePredictions(); for (const prediction of predictions) { if (prediction.confidence >= this.config.predictive.confidenceThreshold) { this.addToWarmingQueue( prediction.provider, prediction.key, prediction.priority, { reason: 'predictive', confidence: prediction.confidence, prediction: prediction } ); } } this.metrics.patterns.predicted += predictions.length; this.emit('predictiveWarming', { predictions: predictions.length, queued: predictions.filter(p => p.confidence >= this.config.predictive.confidenceThreshold ).length }); } catch (error) { logger.error('Predictive warming failed:', error); } } /** * Run time-based warming */ async runTimeBasedWarming() { try { const timeBasedItems = this.patternAnalyzer.getTimeBasedPatterns(); for (const item of timeBasedItems) { if (this.shouldWarmBasedOnTime(item)) { this.addToWarmingQueue( item.provider, item.key, 'medium', { reason: 'time_based', pattern: item.pattern } ); } } this.emit('timeBasedWarming', { items: timeBasedItems.length }); } catch (error) { logger.error('Time-based warming failed:', error); } } /** * Run pattern-based warming */ async runPatternBasedWarming() { try { const patterns = this.patternAnalyzer.getFrequentPatterns(); for (const pattern of patterns) { if (this.shouldWarmBasedOnPattern(pattern)) { this.addToWarmingQueue( pattern.provider, pattern.key, this.calculatePatternPriority(pattern), { reason: 'pattern_based', pattern: pattern } ); } } this.emit('patternBasedWarming', { patterns: patterns.length }); } catch (error) { logger.error('Pattern-based warming failed:', error); } } /** * Run dependency warming */ async runDependencyWarming() { try { const dependencyChains = this.dependencyTracker.getDependencyChains(); for (const chain of dependencyChains) { if (this.shouldWarmDependencyChain(chain)) { for (const item of chain.items) { this.addToWarmingQueue( item.provider, item.key, 'medium', { reason: 'dependency', chain: chain.id, dependencies: chain.dependencies } ); } } } this.emit('dependencyWarming', { chains: dependencyChains.length }); } catch (error) { logger.error('Dependency warming failed:', error); } } /** * Check if item should be warmed based on time patterns */ shouldWarmBasedOnTime(item) { // Implement time-based logic (e.g., daily patterns, peak hours) const now = new Date(); const hour = now.getHours(); // Example: warm popular items before peak hours (9 AM, 1 PM, 5 PM) const peakHours = [9, 13, 17]; const currentMinute = now.getMinutes(); return peakHours.includes(hour) && currentMinute < 30 && item.frequency > this.config.patternAnalysis.minFrequency; } /** * Check if item should be warmed based on patterns */ shouldWarmBasedOnPattern(pattern) { return pattern.frequency > this.config.patternAnalysis.minFrequency && pattern.count >= this.config.patternAnalysis.minRequests && pattern.hitRate < 0.8; // Low hit rate indicates warming opportunity } /** * Calculate priority for pattern-based warming */ calculatePatternPriority(pattern) { const score = (pattern.frequency * 0.4) + ((1 - pattern.hitRate) * 0.4) + (Math.log(pattern.count) * 0.2); if (score > this.config.priority.highPriorityThreshold) return 'high'; if (score > this.config.priority.mediumPriorityThreshold) return 'medium'; return 'low'; } /** * Check if dependency chain should be warmed */ shouldWarmDependencyChain(chain) { return chain.strength > 0.7 && chain.frequency > 0.1; } /** * Force warming of specific cache items */ async forceWarming(provider, keys, priority = 'high') { const warmingPromises = []; for (const key of keys) { warmingPromises.push( this.warmCacheItem({ provider: provider, key: key, priority: priority, timestamp: Date.now(), attempts: 0, metadata: { reason: 'forced' } }) ); } const results = await Promise.allSettled(warmingPromises); this.emit('forcedWarming', { provider: provider, keys: keys, results: results }); return results; } /** * Get warming statistics */ getWarmingStatistics() { return { timestamp: Date.now(), // Overall metrics warming: { ...this.metrics.warming, successRate: this.metrics.warming.total > 0 ? this.metrics.warming.successful / this.metrics.warming.total : 0, byPriority: Object.fromEntries(this.metrics.warming.byPriority), byStrategy: Object.fromEntries(this.metrics.warming.byStrategy) }, // Pattern metrics patterns: { ...this.metrics.patterns, totalPatterns: this.accessPatterns.size, activePatterns: Array.from(this.accessPatterns.values()) .filter(p => p.frequency > this.config.patternAnalysis.minFrequency).length }, // Cache improvement metrics cache: this.metrics.cache, // Resource usage resources: this.metrics.resources, // Queue status queues: { high: this.warmingQueues.get('high')?.length || 0, medium: this.warmingQueues.get('medium')?.length || 0, low: this.warmingQueues.get('low')?.length || 0, total: Array.from(this.warmingQueues.values()) .reduce((sum, queue) => sum + queue.length, 0) }, // State state: { ...this.warmingState, inProgress: this.warmingState.inProgress.size, completed: this.warmingState.completed.size, failed: this.warmingState.failed.size } }; } /** * Get warming recommendations */ getWarmingRecommendations() { const recommendations = []; const stats = this.getWarmingStatistics(); // Success rate recommendations if (stats.warming.successRate < 0.8) { recommendations.push({ type: 'success_rate', priority: 'high', description: 'Cache warming success rate is low', suggestions: [ 'Review warming methods and provider configurations', 'Increase warming timeout limits', 'Check for provider availability issues', 'Optimize warming strategies' ] }); } // Queue size recommendations if (stats.queues.total > 100) { recommendations.push({ type: 'queue_size', priority: 'medium', description: 'Large warming queue detected', suggestions: [ 'Increase warming concurrency', 'Review warming priorities', 'Optimize warming performance', 'Consider reducing warming frequency' ] }); } // Pattern utilization recommendations if (stats.patterns.activePatterns < stats.patterns.totalPatterns * 0.5) { recommendations.push({ type: 'pattern_utilization', priority: 'low', description: 'Many patterns are not being utilized for warming', suggestions: [ 'Review pattern analysis thresholds', 'Optimize pattern detection algorithms', 'Consider pattern cleanup for unused patterns' ] }); } return recommendations; } /** * Shutdown cache warmer */ async shutdown() { try { logger.info('Shutting down cache warmer'); // Clear monitoring intervals for (const interval of this.monitoringIntervals) { clearInterval(interval); } // Shutdown sub-systems await this.predictor.shutdown(); await this.patternAnalyzer.shutdown(); await this.dependencyTracker.shutdown(); // Clear data structures this.cacheProviders.clear(); this.accessPatterns.clear(); this.requestHistory = []; this.warmingQueues.forEach(queue => queue.length = 0); this.initialized = false; this.emit('shutdown'); logger.info('Cache warmer shutdown completed'); } catch (error) { logger.error('Error during cache warmer shutdown:', error); throw error; } } } /** * Cache Prediction Engine */ class CachePredictionEngine { constructor(warmer) { this.warmer = warmer; this.models = new Map(); this.predictions = []; } async initialize() { logger.debug('Cache prediction engine initialized'); } async generatePredictions() { const predictions = []; // Simple pattern-based prediction for (const [key, pattern] of this.warmer.accessPatterns) { if (pattern.timePattern.length >= 3) { const prediction = this.predictNextAccess(pattern); if (prediction) { predictions.push(prediction); } } } // Limit predictions return predictions.slice(0, this.warmer.config.predictive.maxPredictions); } predictNextAccess(pattern) { // Simple time-based prediction const timePattern = pattern.timePattern.slice(-10); if (timePattern.length < 3) return null; // Calculate average interval const intervals = []; for (let i = 1; i < timePattern.length; i++) { intervals.push(timePattern[i] - timePattern[i - 1]); } const avgInterval = intervals.reduce((sum, interval) => sum + interval, 0) / intervals.length; const nextPredictedTime = pattern.lastAccess + avgInterval; // Check if prediction is within look-ahead window const now = Date.now(); const lookAheadTime = this.warmer.config.predictive.lookAheadTime; if (nextPredictedTime > now && nextPredictedTime < now + lookAheadTime) { return { provider: pattern.provider, key: pattern.key, predictedTime: nextPredictedTime, confidence: Math.min(0.9, pattern.frequency * 10), // Simple confidence priority: pattern.frequency > 0.1 ? 'high' : 'medium' }; } return null; } async shutdown() { this.models.clear(); this.predictions = []; logger.debug('Cache prediction engine shutdown'); } } /** * Access Pattern Analyzer */ class AccessPatternAnalyzer { constructor(warmer) { this.warmer = warmer; this.timeBasedPatterns = new Map(); } async initialize() { logger.debug('Access pattern analyzer initialized'); } async analyzePatterns() { this.analyzeTimeBasedPatterns(); this.analyzeFrequencyPatterns(); this.warmer.metrics.patterns.detected++; } analyzeTimeBasedPatterns() { // Analyze patterns based on time of day, day of week, etc. for (const [key, pattern] of this.warmer.accessPatterns) { const timeAnalysis = this.analyzeTimePattern(pattern.timePattern); if (timeAnalysis.strength > 0.6) { this.timeBasedPatterns.set(key, { ...pattern, timeAnalysis: timeAnalysis }); } } } analyzeTimePattern(timePattern) { if (timePattern.length < 5) return { strength: 0 }; // Simple analysis: check for regular intervals const intervals = []; for (let i = 1; i < timePattern.length; i++) { intervals.push(timePattern[i] - timePattern[i - 1]); } // Calculate coefficient of variation const mean = intervals.reduce((sum, val) => sum + val, 0) / intervals.length; const variance = intervals.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / intervals.length; const stdDev = Math.sqrt(variance); const cv = mean > 0 ? stdDev / mean : 1; // Lower CV indicates more regular pattern const strength = Math.max(0, 1 - cv); return { strength: strength, averageInterval: mean, regularity: 1 - cv, intervalCount: intervals.length }; } analyzeFrequencyPatterns() { // Already handled in updateAccessPatterns } getTimeBasedPatterns() { return Array.from(this.timeBasedPatterns.values()); } getFrequentPatterns() { return Array.from(this.warmer.accessPatterns.values()) .filter(pattern => pattern.frequency > this.warmer.config.patternAnalysis.minFrequency && pattern.count >= this.warmer.config.patternAnalysis.minRequests ); } async shutdown() { this.timeBasedPatterns.clear(); logger.debug('Access pattern analyzer shutdown'); } } /** * Dependency Tracker */ class DependencyTracker { constructor(warmer) { this.warmer = warmer; this.dependencies = new Map(); this.chains = new Map(); } async initialize() { logger.debug('Dependency tracker initialized'); } trackAccess(access) { // Track access dependencies based on temporal proximity const recentAccesses = this.warmer.requestHistory.slice(-10); for (const recentAccess of recentAccesses) { if (recentAccess.timestamp < access.timestamp - 60000) continue; // 1 minute window if (recentAccess.key === access.key) continue; // Same key const dependencyKey = `${recentAccess.provider}:${recentAccess.key}`; const dependentKey = `${access.provider}:${access.key}`; this.updateDependency(dependencyKey, dependentKey); } } updateDependency(dependencyKey, dependentKey) { if (!this.dependencies.has(dependencyKey)) { this.dependencies.set(dependencyKey, new Map()); } const dependents = this.dependencies.get(dependencyKey); const count = dependents.get(dependentKey) || 0; dependents.set(dependentKey, count + 1); } getDependencyChains() { const chains = []; for (const [dependency, dependents] of this.dependencies) { const totalCount = Array.from(dependents.values()).reduce((sum, count) => sum + count, 0); if (totalCount >= 3) { // Minimum strength threshold chains.push({ id: dependency, root: dependency, items: Array.from(dependents.keys()).map(key => { const [provider, cacheKey] = key.split(':'); return { provider, key: cacheKey }; }), strength: Math.min(1, totalCount / 10), frequency: totalCount / this.warmer.requestHistory.length, dependencies: Object.fromEntries(dependents) }); } } return chains; } async shutdown() { this.dependencies.clear(); this.chains.clear(); logger.debug('Dependency tracker shutdown'); } } export default CacheWarmer;

Latest Blog Posts

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/sascodiego/KGsMCP'

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