UNPKG

@ooples/token-optimizer-mcp

Version:

Intelligent context window optimization for Claude Code - store content externally via caching and compression, freeing up your context window for what matters

1,285 lines 58.9 kB
/** * CacheAnalytics - Comprehensive Cache Analytics & Monitoring * * Real-time analytics and reporting for cache performance and usage. * Provides visualization, trend analysis, alerting, and cost analysis capabilities. * * Operations: * 1. dashboard - Get real-time dashboard data * 2. metrics - Get detailed metrics * 3. trends - Analyze trends over time * 4. alerts - Configure and check alerts * 5. heatmap - Generate access heatmap * 6. bottlenecks - Identify performance bottlenecks * 7. cost-analysis - Analyze caching costs * 8. export-data - Export analytics data * * Token Reduction Target: 88%+ */ import { EventEmitter } from 'events'; import { writeFileSync } from 'fs'; // ============================================================================ // Main Implementation // ============================================================================ /** * CacheAnalytics - Comprehensive analytics and monitoring tool */ export class CacheAnalyticsTool extends EventEmitter { cache; tokenCounter; metrics; // Configuration alertConfigs = new Map(); historicalData = new Map(); maxHistoricalEntries = 1000; // Time-series data for trends timeSeriesData = new Map(); // Key access tracking keyAccessLog = new Map(); constructor(cache, tokenCounter, metrics) { super(); this.cache = cache; this.tokenCounter = tokenCounter; this.metrics = metrics; this.initializeDefaults(); } /** * Initialize default alert configurations */ initializeDefaults() { this.alertConfigs.set('high-error-rate', { metric: 'errorRate', condition: 'gt', threshold: 5.0, severity: 'critical', enabled: true, }); this.alertConfigs.set('low-hit-rate', { metric: 'hitRate', condition: 'lt', threshold: 70.0, severity: 'warning', enabled: true, }); this.alertConfigs.set('high-latency', { metric: 'latencyP95', condition: 'gt', threshold: 100.0, severity: 'warning', enabled: true, }); this.alertConfigs.set('memory-pressure', { metric: 'memoryUtilization', condition: 'gt', threshold: 80.0, severity: 'warning', enabled: true, }); } /** * Main entry point for cache analytics operations */ async run(options) { const startTime = Date.now(); const { operation, useCache = true } = options; // Generate cache key for cacheable operations let cacheKey = null; if (useCache && this.isCacheableOperation(operation)) { cacheKey = `cache-analytics:${JSON.stringify({ operation, ...this.getCacheKeyParams(options), })}`; // Check cache const cached = this.cache.get(cacheKey); if (cached) { try { const data = JSON.parse(cached); const tokensSaved = this.tokenCounter.count(JSON.stringify(data)).tokens; return { success: true, operation, data, metadata: { tokensUsed: 0, tokensSaved, cacheHit: true, executionTime: Date.now() - startTime, }, }; } catch { // Cache parse error, continue with fresh execution } } } // Execute operation let data; try { switch (operation) { case 'dashboard': data = { dashboard: await this.getDashboard(options) }; break; case 'metrics': data = { metrics: await this.getMetrics(options) }; break; case 'trends': data = { trends: await this.analyzeTrends(options) }; break; case 'alerts': data = { alerts: await this.checkAlerts(options) }; break; case 'heatmap': data = { heatmap: await this.generateHeatmap(options) }; break; case 'bottlenecks': data = { bottlenecks: await this.identifyBottlenecks(options) }; break; case 'cost-analysis': data = { costAnalysis: await this.analyzeCosts(options) }; break; case 'export-data': data = { exportData: await this.exportData(options) }; break; default: throw new Error(`Unknown operation: ${operation}`); } // Calculate tokens and cache result const tokensUsed = this.tokenCounter.count(JSON.stringify(data)).tokens; if (cacheKey && useCache) { const serialized = JSON.stringify(data); this.cache.set(cacheKey, serialized, tokensUsed, serialized.length); } // Record metrics this.metrics.record({ operation: `analytics_${operation}`, duration: Date.now() - startTime, success: true, cacheHit: false, inputTokens: 0, outputTokens: tokensUsed, cachedTokens: 0, savedTokens: 0, metadata: { operation }, }); return { success: true, operation, data, metadata: { tokensUsed, tokensSaved: 0, cacheHit: false, executionTime: Date.now() - startTime, }, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.metrics.record({ operation: `analytics_${operation}`, duration: Date.now() - startTime, success: false, cacheHit: false, inputTokens: 0, outputTokens: 0, cachedTokens: 0, savedTokens: 0, metadata: { operation, error: errorMessage }, }); throw error; } } // ============================================================================ // Dashboard Operations // ============================================================================ /** * Get real-time dashboard data */ async getDashboard(options) { const now = Date.now(); const timeRange = options.timeRange || { start: now - 3600000, end: now }; // Gather all metrics const performance = this.getPerformanceMetrics(timeRange); const usage = this.getUsageMetrics(timeRange); const efficiency = this.getEfficiencyMetrics(timeRange); const cost = this.getCostMetrics(timeRange); const health = this.getHealthMetrics(timeRange); const recentActivity = this.getRecentActivity(10); const dashboard = { timestamp: now, performance, usage, efficiency, cost, health, recentActivity, }; // Store for trend analysis this.historicalData.set(now, dashboard); if (this.historicalData.size > this.maxHistoricalEntries) { const oldestKey = Array.from(this.historicalData.keys()).sort((a, b) => a - b)[0]; this.historicalData.delete(oldestKey); } // Update time-series data this.updateTimeSeries('hitRate', now, performance.hitRate); this.updateTimeSeries('latency', now, performance.latencyP95); this.updateTimeSeries('throughput', now, performance.throughput); this.emit('dashboard-updated', dashboard); return dashboard; } /** * Get performance metrics */ getPerformanceMetrics(timeRange) { const stats = this.metrics.getCacheStats(timeRange.start); const percentiles = this.metrics.getPerformancePercentiles(timeRange.start); const duration = (timeRange.end - timeRange.start) / 1000 || 1; return { hitRate: stats.cacheHitRate, latencyP50: percentiles.p50, latencyP95: percentiles.p95, latencyP99: percentiles.p99, throughput: stats.totalOperations / duration, operationsPerSecond: stats.totalOperations / duration, averageResponseTime: stats.averageDuration, }; } /** * Get usage metrics */ getUsageMetrics(timeRange) { const cacheStats = this.cache.getStats(); const operations = this.metrics.getOperations(timeRange.start); // Calculate key access frequency const keyAccessFrequency = new Map(); for (const op of operations) { const key = this.extractKeyFromMetadata(op.metadata); if (key) { keyAccessFrequency.set(key, (keyAccessFrequency.get(key) || 0) + 1); } } // Get top accessed keys const topAccessedKeys = Array.from(keyAccessFrequency.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, 10) .map(([key, hits]) => ({ key, hits })); // Size distribution (simulated based on total entries) const valueSizeDistribution = { small: Math.floor(cacheStats.totalEntries * 0.6), medium: Math.floor(cacheStats.totalEntries * 0.25), large: Math.floor(cacheStats.totalEntries * 0.1), xlarge: Math.floor(cacheStats.totalEntries * 0.05), }; // Get recently added keys const recentlyAdded = operations .filter((op) => op.operation.includes('set')) .slice(-10) .map((op) => ({ key: this.extractKeyFromMetadata(op.metadata) || 'unknown', timestamp: op.timestamp, })); return { totalKeys: cacheStats.totalEntries, totalSize: cacheStats.totalCompressedSize, keyAccessFrequency, valueSizeDistribution, topAccessedKeys, recentlyAdded, }; } /** * Get efficiency metrics */ getEfficiencyMetrics(timeRange) { const cacheStats = this.cache.getStats(); const operations = this.metrics.getOperations(timeRange.start); // Calculate eviction rate const evictionOps = operations.filter((op) => op.operation.includes('evict')).length; const totalOps = operations.length || 1; const evictionRate = (evictionOps / totalOps) * 100; // Eviction patterns const evictionPatterns = [ { reason: 'TTL Expired', count: Math.floor(evictionOps * 0.5), percentage: 50, trend: 'stable', }, { reason: 'Size Limit', count: Math.floor(evictionOps * 0.3), percentage: 30, trend: this.calculateEvictionTrend('size'), }, { reason: 'Manual', count: Math.floor(evictionOps * 0.2), percentage: 20, trend: 'stable', }, ]; return { memoryUtilization: (cacheStats.totalCompressedSize / (500 * 1024 * 1024)) * 100, evictionRate, evictionPatterns, compressionRatio: cacheStats.compressionRatio, fragmentationIndex: this.calculateFragmentation(), }; } /** * Get cost metrics */ getCostMetrics(timeRange) { const cacheStats = this.cache.getStats(); const operations = this.metrics.getOperations(timeRange.start); // Cost calculations (simulated pricing) const memoryCostPerGB = 0.1; // $0.10 per GB-hour const diskCostPerGB = 0.02; // $0.02 per GB-hour const networkCostPerGB = 0.05; // $0.05 per GB const operationCost = 0.000001; // $0.000001 per operation const memoryGB = cacheStats.totalCompressedSize / (1024 * 1024 * 1024); const hours = (timeRange.end - timeRange.start) / 3600000; const memoryCost = memoryGB * memoryCostPerGB * hours; const diskCost = memoryGB * diskCostPerGB * hours; const networkCost = memoryGB * networkCostPerGB; const totalCost = memoryCost + diskCost + networkCost + operations.length * operationCost; // Calculate cost trend from historical data const costTrend = this.calculateCostTrend(totalCost); return { memoryCost, diskCost, networkCost, totalCost, costPerOperation: totalCost / (operations.length || 1), costTrend, }; } /** * Get health metrics */ getHealthMetrics(timeRange) { const operations = this.metrics.getOperations(timeRange.start); const stats = this.metrics.getCacheStats(timeRange.start); const errorOps = operations.filter((op) => !op.success).length; const timeoutOps = operations.filter((op) => op.duration > 1000).length; const totalOps = operations.length || 1; const errorRate = (errorOps / totalOps) * 100; const timeoutRate = (timeoutOps / totalOps) * 100; const criticalIssues = []; if (errorRate > 5) { criticalIssues.push(`High error rate: ${errorRate.toFixed(2)}%`); } if (timeoutRate > 10) { criticalIssues.push(`High timeout rate: ${timeoutRate.toFixed(2)}%`); } if (stats.cacheHitRate < 50) { criticalIssues.push(`Low cache hit rate: ${stats.cacheHitRate.toFixed(2)}%`); } // Calculate health score (0-100) const healthScore = Math.max(0, 100 - errorRate * 2 - timeoutRate * 1.5 - (100 - stats.cacheHitRate) * 0.5); return { errorRate, timeoutRate, fragmentationLevel: this.calculateFragmentation(), warningCount: criticalIssues.length, criticalIssues, healthScore, }; } /** * Get recent activity */ getRecentActivity(limit) { const operations = this.metrics.getOperations(); return operations.slice(-limit).map((op) => ({ timestamp: op.timestamp, operation: op.operation, key: this.extractKeyFromMetadata(op.metadata), duration: op.duration, status: op.success ? 'success' : 'error', })); } // ============================================================================ // Metrics Operations // ============================================================================ /** * Get detailed metrics */ async getMetrics(options) { const now = Date.now(); const timeRange = options.timeRange || { start: now - 3600000, end: now }; const operations = this.metrics.getOperations(timeRange.start); const metricTypes = options.metricTypes || [ 'performance', 'usage', 'efficiency', 'cost', 'health', ]; const metrics = { timestamp: now, timeRange, }; if (metricTypes.includes('performance')) { metrics.performance = this.getPerformanceMetrics(timeRange); } if (metricTypes.includes('usage')) { metrics.usage = this.getUsageMetrics(timeRange); } if (metricTypes.includes('efficiency')) { metrics.efficiency = this.getEfficiencyMetrics(timeRange); } if (metricTypes.includes('cost')) { metrics.cost = this.getCostMetrics(timeRange); } if (metricTypes.includes('health')) { metrics.health = this.getHealthMetrics(timeRange); } // Aggregated data const successfulOps = operations.filter((op) => op.success).length; const failedOps = operations.length - successfulOps; const totalDuration = operations.reduce((sum, op) => sum + op.duration, 0); const cacheHits = operations.filter((op) => op.cacheHit).length; const tokensSaved = operations.reduce((sum, op) => sum + (op.savedTokens || 0), 0); const compressionSavings = operations.reduce((sum, op) => sum + ((op.outputTokens ?? 0) - (op.cachedTokens ?? 0) || 0), 0); metrics.aggregatedData = { totalOperations: operations.length, successfulOperations: successfulOps, failedOperations: failedOps, averageDuration: totalDuration / (operations.length || 1), totalCacheHits: cacheHits, totalCacheMisses: operations.length - cacheHits, tokensSaved, compressionSavings, }; this.emit('metrics-collected', metrics); return metrics; } // ============================================================================ // Trend Analysis // ============================================================================ /** * Analyze trends over time */ async analyzeTrends(options) { const now = Date.now(); const timeRange = options.timeRange || { start: now - 86400000, end: now }; // Last 24 hours // Get current and previous metrics const currentMetrics = await this.getMetrics({ ...options, timeRange }); const previousRange = this.getPreviousTimeRange(timeRange, options.compareWith || 'previous-period'); const previousMetrics = await this.getMetrics({ ...options, timeRange: previousRange, }); // Calculate trend metrics const trendMetrics = this.calculateTrendMetrics(currentMetrics, previousMetrics); // Detect anomalies const anomalies = this.detectAnomalies(timeRange); // Generate predictions const predictions = this.generatePredictions(timeRange); // Calculate regression const regression = this.calculateRegression(timeRange); // Detect seasonality const seasonality = this.detectSeasonality(timeRange); const analysis = { timestamp: now, timeRange, metrics: trendMetrics, anomalies, predictions, regression, seasonality, }; this.emit('trends-analyzed', analysis); return analysis; } /** * Calculate trend metrics */ calculateTrendMetrics(current, previous) { const metrics = []; if (current.performance && previous.performance) { metrics.push(this.createTrendMetric('Hit Rate', current.performance.hitRate, previous.performance.hitRate), this.createTrendMetric('Latency P95', current.performance.latencyP95, previous.performance.latencyP95), this.createTrendMetric('Throughput', current.performance.throughput, previous.performance.throughput)); } if (current.health && previous.health) { metrics.push(this.createTrendMetric('Health Score', current.health.healthScore, previous.health.healthScore), this.createTrendMetric('Error Rate', current.health.errorRate, previous.health.errorRate)); } return metrics; } /** * Create trend metric */ createTrendMetric(name, current, previous) { const change = current - previous; const changePercent = previous !== 0 ? (change / previous) * 100 : 0; const velocity = change / (previous || 1); let trend; if (Math.abs(changePercent) < 5) { trend = 'stable'; } else if (change > 0) { trend = 'up'; } else { trend = 'down'; } return { name, current, previous, change, changePercent, trend, velocity, }; } /** * Detect anomalies */ detectAnomalies(timeRange) { const anomalies = []; const operations = this.metrics.getOperations(timeRange.start); // Calculate statistics const durations = operations.map((op) => op.duration); const mean = durations.reduce((a, b) => a + b, 0) / (durations.length || 1); const variance = durations.reduce((sum, d) => sum + Math.pow(d - mean, 2), 0) / (durations.length || 1); const stdDev = Math.sqrt(variance); // Detect duration anomalies for (const op of operations) { const zScore = (op.duration - mean) / stdDev; if (Math.abs(zScore) > 3) { // 3 sigma rule anomalies.push({ timestamp: op.timestamp, metric: 'duration', value: op.duration, expected: mean, deviation: zScore, severity: Math.abs(zScore) > 4 ? 'high' : 'medium', confidence: 1 - 1 / Math.abs(zScore), }); } } // Detect hit rate anomalies const hitRateSeries = Array.from(this.timeSeriesData.get('hitRate') || []).slice(-20); if (hitRateSeries.length > 5) { const avgHitRate = hitRateSeries.reduce((sum, p) => sum + p.value, 0) / hitRateSeries.length; const currentHitRate = hitRateSeries[hitRateSeries.length - 1].value; if (Math.abs(currentHitRate - avgHitRate) > 20) { anomalies.push({ timestamp: Date.now(), metric: 'hitRate', value: currentHitRate, expected: avgHitRate, deviation: (currentHitRate - avgHitRate) / avgHitRate, severity: 'medium', confidence: 0.8, }); } } return anomalies; } /** * Generate predictions */ generatePredictions(_timeRange) { const predictions = []; const now = Date.now(); const horizon = 3600000; // 1 hour ahead // Predict hit rate const hitRateSeries = Array.from(this.timeSeriesData.get('hitRate') || []).slice(-20); if (hitRateSeries.length > 5) { const trend = this.calculateSimpleTrend(hitRateSeries.map((p) => p.value)); const lastValue = hitRateSeries[hitRateSeries.length - 1].value; const predicted = lastValue + trend; predictions.push({ metric: 'hitRate', timestamp: now + horizon, predictedValue: Math.max(0, Math.min(100, predicted)), confidenceInterval: { lower: Math.max(0, predicted - 10), upper: Math.min(100, predicted + 10), }, confidence: 0.7, }); } // Predict throughput const throughputSeries = Array.from(this.timeSeriesData.get('throughput') || []).slice(-20); if (throughputSeries.length > 5) { const trend = this.calculateSimpleTrend(throughputSeries.map((p) => p.value)); const lastValue = throughputSeries[throughputSeries.length - 1].value; const predicted = Math.max(0, lastValue + trend); predictions.push({ metric: 'throughput', timestamp: now + horizon, predictedValue: predicted, confidenceInterval: { lower: Math.max(0, predicted * 0.8), upper: predicted * 1.2, }, confidence: 0.65, }); } return predictions; } /** * Calculate simple linear trend */ calculateSimpleTrend(values) { if (values.length < 2) return 0; const n = values.length; const xMean = (n - 1) / 2; const yMean = values.reduce((a, b) => a + b, 0) / n; let numerator = 0; let denominator = 0; for (let i = 0; i < n; i++) { numerator += (i - xMean) * (values[i] - yMean); denominator += Math.pow(i - xMean, 2); } return denominator !== 0 ? numerator / denominator : 0; } /** * Calculate regression */ calculateRegression(_timeRange) { const hitRateSeries = Array.from(this.timeSeriesData.get('hitRate') || []).slice(-50); if (hitRateSeries.length < 2) { return { slope: 0, intercept: 0, rSquared: 0, equation: 'y = 0', }; } const n = hitRateSeries.length; const xValues = hitRateSeries.map((_, i) => i); const yValues = hitRateSeries.map((p) => p.value); const xMean = xValues.reduce((a, b) => a + b, 0) / n; const yMean = yValues.reduce((a, b) => a + b, 0) / n; let numerator = 0; let denominator = 0; for (let i = 0; i < n; i++) { numerator += (xValues[i] - xMean) * (yValues[i] - yMean); denominator += Math.pow(xValues[i] - xMean, 2); } const slope = denominator !== 0 ? numerator / denominator : 0; const intercept = yMean - slope * xMean; // Calculate R-squared let ssRes = 0; let ssTot = 0; for (let i = 0; i < n; i++) { const predicted = slope * xValues[i] + intercept; ssRes += Math.pow(yValues[i] - predicted, 2); ssTot += Math.pow(yValues[i] - yMean, 2); } const rSquared = ssTot !== 0 ? 1 - ssRes / ssTot : 0; return { slope, intercept, rSquared, equation: `y = ${slope.toFixed(2)}x + ${intercept.toFixed(2)}`, }; } /** * Detect seasonality */ detectSeasonality(_timeRange) { const series = Array.from(this.timeSeriesData.get('throughput') || []).slice(-100); if (series.length < 20) { return { detected: false, period: 0, strength: 0, peaks: [], troughs: [], }; } // Simple peak detection const values = series.map((p) => p.value); const peaks = []; const troughs = []; for (let i = 1; i < values.length - 1; i++) { if (values[i] > values[i - 1] && values[i] > values[i + 1]) { peaks.push(i); } if (values[i] < values[i - 1] && values[i] < values[i + 1]) { troughs.push(i); } } // Calculate average period between peaks let avgPeriod = 0; if (peaks.length > 1) { const periods = peaks.slice(1).map((p, i) => p - peaks[i]); avgPeriod = periods.reduce((a, b) => a + b, 0) / periods.length; } const detected = peaks.length > 2 && avgPeriod > 0; const strength = detected ? Math.min(1, peaks.length / 10) : 0; return { detected, period: Math.round(avgPeriod), strength, peaks, troughs, }; } // ============================================================================ // Alert Operations // ============================================================================ /** * Check alerts and return triggered ones */ async checkAlerts(options) { const alerts = []; const now = Date.now(); const timeRange = options.timeRange || { start: now - 3600000, end: now }; // Add custom alert config if provided if (options.alertConfig) { this.alertConfigs.set(`custom-${now}`, options.alertConfig); } // Get current metrics const currentMetrics = await this.getMetrics({ ...options, timeRange }); // Check each alert configuration for (const [id, config] of Array.from(this.alertConfigs.entries())) { if (!config.enabled) continue; const value = this.extractMetricValue(config.metric, currentMetrics); const triggered = this.evaluateAlertCondition(value, config.condition, config.threshold); if (triggered) { alerts.push({ id, type: options.alertType || 'threshold', metric: config.metric, severity: config.severity, message: `${config.metric} ${config.condition} ${config.threshold} (current: ${value.toFixed(2)})`, timestamp: now, value, threshold: config.threshold, triggered: true, }); } } // Check for anomaly alerts const anomalies = this.detectAnomalies(timeRange); for (const anomaly of anomalies) { if (anomaly.severity === 'high') { alerts.push({ id: `anomaly-${anomaly.timestamp}`, type: 'anomaly', metric: anomaly.metric, severity: 'warning', message: `Anomaly detected in ${anomaly.metric}: ${anomaly.value.toFixed(2)} (expected: ${anomaly.expected.toFixed(2)})`, timestamp: anomaly.timestamp, value: anomaly.value, triggered: true, }); } } this.emit('alerts-checked', { count: alerts.length, alerts }); return alerts; } /** * Extract metric value from metrics collection */ extractMetricValue(metricName, metrics) { if (metricName === 'hitRate' && metrics.performance) { return metrics.performance.hitRate; } if (metricName === 'errorRate' && metrics.health) { return metrics.health.errorRate; } if (metricName === 'latencyP95' && metrics.performance) { return metrics.performance.latencyP95; } if (metricName === 'memoryUtilization' && metrics.efficiency) { return metrics.efficiency.memoryUtilization; } return 0; } /** * Evaluate alert condition */ evaluateAlertCondition(value, condition, threshold) { switch (condition) { case 'gt': return value > threshold; case 'lt': return value < threshold; case 'eq': return Math.abs(value - threshold) < 0.01; case 'ne': return Math.abs(value - threshold) >= 0.01; default: return false; } } // ============================================================================ // Heatmap Generation // ============================================================================ /** * Generate access heatmap */ async generateHeatmap(options) { const heatmapType = options.heatmapType || 'temporal'; const resolution = options.resolution || 'medium'; const now = Date.now(); const timeRange = options.timeRange || { start: now - 86400000, end: now }; let heatmap; switch (heatmapType) { case 'temporal': heatmap = this.generateTemporalHeatmap(timeRange, resolution); break; case 'key-correlation': heatmap = this.generateKeyCorrelationHeatmap(timeRange, resolution); break; case 'memory': heatmap = this.generateMemoryHeatmap(timeRange, resolution); break; default: throw new Error(`Unknown heatmap type: ${heatmapType}`); } this.emit('heatmap-generated', heatmap); return heatmap; } /** * Generate temporal heatmap (hour x day of week) */ generateTemporalHeatmap(timeRange, _resolution) { const operations = this.metrics.getOperations(timeRange.start); // Create 24x7 matrix (hour x day of week) const data = Array(24) .fill(0) .map(() => Array(7).fill(0)); // Count operations per hour per day for (const op of operations) { const date = new Date(op.timestamp); const hour = date.getHours(); const dayOfWeek = date.getDay(); data[hour][dayOfWeek]++; } // Find hotspots const hotspots = []; let maxIntensity = 0; let totalIntensity = 0; let cellCount = 0; for (let h = 0; h < 24; h++) { for (let d = 0; d < 7; d++) { const value = data[h][d]; totalIntensity += value; cellCount++; if (value > maxIntensity) { maxIntensity = value; } if (value > 0) { hotspots.push({ x: d, y: h, value }); } } } hotspots.sort((a, b) => b.value - a.value); const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; const hours = Array.from({ length: 24 }, (_, i) => `${i}:00`); return { type: 'temporal', dimensions: { width: 7, height: 24 }, data, labels: { x: days, y: hours }, colorScale: { min: 0, max: maxIntensity }, summary: { hotspots: hotspots.slice(0, 5), avgIntensity: totalIntensity / cellCount, maxIntensity, }, }; } /** * Generate key correlation heatmap */ generateKeyCorrelationHeatmap(timeRange, _resolution) { const operations = this.metrics.getOperations(timeRange.start); // Get top keys const keyFrequency = new Map(); for (const op of operations) { const key = this.extractKeyFromMetadata(op.metadata); if (key) { keyFrequency.set(key, (keyFrequency.get(key) || 0) + 1); } } const topKeys = Array.from(keyFrequency.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, 10) .map(([key]) => key); // Calculate correlation matrix const size = topKeys.length; const data = Array(size) .fill(0) .map(() => Array(size).fill(0)); // Calculate co-occurrence const windowSize = 1000; // 1 second for (let i = 0; i < operations.length - 1; i++) { const key1 = this.extractKeyFromMetadata(operations[i].metadata); if (!key1 || !topKeys.includes(key1)) continue; for (let j = i + 1; j < operations.length; j++) { if (operations[j].timestamp - operations[i].timestamp > windowSize) break; const key2 = this.extractKeyFromMetadata(operations[j].metadata); if (!key2 || !topKeys.includes(key2)) continue; const idx1 = topKeys.indexOf(key1); const idx2 = topKeys.indexOf(key2); data[idx1][idx2]++; data[idx2][idx1]++; } } // Normalize let maxValue = 0; for (const row of data) { for (const val of row) { if (val > maxValue) maxValue = val; } } if (maxValue > 0) { for (let i = 0; i < size; i++) { for (let j = 0; j < size; j++) { data[i][j] = data[i][j] / maxValue; } } } // Find hotspots const hotspots = []; let totalIntensity = 0; for (let i = 0; i < size; i++) { for (let j = 0; j < size; j++) { if (i !== j && data[i][j] > 0.3) { hotspots.push({ x: j, y: i, value: data[i][j] }); } totalIntensity += data[i][j]; } } hotspots.sort((a, b) => b.value - a.value); return { type: 'key-correlation', dimensions: { width: size, height: size }, data, labels: { x: topKeys, y: topKeys }, colorScale: { min: 0, max: 1 }, summary: { hotspots: hotspots.slice(0, 5), avgIntensity: totalIntensity / (size * size), maxIntensity: 1, }, }; } /** * Generate memory usage heatmap */ generateMemoryHeatmap(_timeRange, _resolution) { const cacheStats = this.cache.getStats(); // Create simple memory layout visualization (10x10 grid) const size = 10; const data = Array(size) .fill(0) .map(() => Array(size).fill(0)); // Simulate memory distribution const usedCells = Math.floor((cacheStats.totalCompressedSize / (500 * 1024 * 1024)) * 100); for (let i = 0; i < usedCells && i < 100; i++) { const x = i % size; const y = Math.floor(i / size); data[y][x] = 0.5 + Math.random() * 0.5; } // Find hotspots const hotspots = []; let totalIntensity = 0; let maxIntensity = 0; for (let i = 0; i < size; i++) { for (let j = 0; j < size; j++) { const value = data[i][j]; totalIntensity += value; if (value > maxIntensity) maxIntensity = value; if (value > 0.7) { hotspots.push({ x: j, y: i, value }); } } } hotspots.sort((a, b) => b.value - a.value); return { type: 'memory', dimensions: { width: size, height: size }, data, labels: { x: Array.from({ length: size }, (_, i) => `Block ${i}`), y: Array.from({ length: size }, (_, i) => `Tier ${i}`), }, colorScale: { min: 0, max: 1 }, summary: { hotspots: hotspots.slice(0, 5), avgIntensity: totalIntensity / (size * size), maxIntensity, }, }; } // ============================================================================ // Bottleneck Identification // ============================================================================ /** * Identify performance bottlenecks */ async identifyBottlenecks(options) { const bottlenecks = []; const now = Date.now(); const timeRange = options.timeRange || { start: now - 3600000, end: now }; const operations = this.metrics.getOperations(timeRange.start); const percentiles = this.metrics.getPerformancePercentiles(timeRange.start); // Check for slow operations const slowOps = operations.filter((op) => op.duration > percentiles.p95); if (slowOps.length > operations.length * 0.05) { bottlenecks.push({ type: 'slow-operation', severity: 'high', description: `${slowOps.length} operations slower than P95 (${percentiles.p95}ms)`, impact: (slowOps.length / operations.length) * 100, recommendation: 'Consider optimizing slow operations or increasing cache size', metrics: { current: slowOps.length, threshold: operations.length * 0.05, duration: percentiles.p95, }, }); } // Check for hot keys const keyFrequency = new Map(); for (const op of operations) { const key = this.extractKeyFromMetadata(op.metadata); if (key) { keyFrequency.set(key, (keyFrequency.get(key) || 0) + 1); } } const hotKeys = Array.from(keyFrequency.entries()) .filter(([_, count]) => count > operations.length * 0.1) .map(([key]) => key); if (hotKeys.length > 0) { bottlenecks.push({ type: 'hot-key', severity: 'medium', description: `${hotKeys.length} keys accessed more than 10% of the time`, impact: 50, recommendation: 'Consider implementing read-through caching or sharding for hot keys', affectedKeys: hotKeys, metrics: { current: hotKeys.length, threshold: 3, duration: 0, }, }); } // Check for memory pressure const cacheStats = this.cache.getStats(); const memoryUtilization = (cacheStats.totalCompressedSize / (500 * 1024 * 1024)) * 100; if (memoryUtilization > 80) { bottlenecks.push({ type: 'memory-pressure', severity: 'high', description: `Memory utilization at ${memoryUtilization.toFixed(1)}%`, impact: memoryUtilization, recommendation: 'Increase cache size or implement more aggressive eviction policies', metrics: { current: memoryUtilization, threshold: 80, duration: 0, }, }); } // Check for high eviction rate const evictionOps = operations.filter((op) => op.operation.includes('evict')).length; const evictionRate = (evictionOps / operations.length) * 100; if (evictionRate > 20) { bottlenecks.push({ type: 'high-eviction', severity: 'medium', description: `High eviction rate: ${evictionRate.toFixed(1)}%`, impact: evictionRate, recommendation: 'Consider increasing TTL or cache size to reduce evictions', metrics: { current: evictionRate, threshold: 20, duration: 0, }, }); } this.emit('bottlenecks-identified', { count: bottlenecks.length }); return bottlenecks; } // ============================================================================ // Cost Analysis // ============================================================================ /** * Analyze caching costs */ async analyzeCosts(options) { const now = Date.now(); const timeRange = options.timeRange || { start: now - 86400000, end: now }; const cacheStats = this.cache.getStats(); const operations = this.metrics.getOperations(timeRange.start); // Storage costs const memoryGB = cacheStats.totalCompressedSize / (1024 * 1024 * 1024); const hours = (timeRange.end - timeRange.start) / 3600000; const storage = { memoryCost: memoryGB * 0.1 * hours, diskCost: memoryGB * 0.02 * hours, totalStorage: cacheStats.totalCompressedSize, utilizationPercent: (cacheStats.totalCompressedSize / (500 * 1024 * 1024)) * 100, }; // Network costs const totalTraffic = operations.length * 1024; // Estimate 1KB per operation const network = { ingressCost: totalTraffic * 0.00005, egressCost: totalTraffic * 0.00009, totalTraffic, bandwidthUtilization: 0.5, }; // Compute costs const compute = { cpuCost: operations.length * 0.000001, operationCost: operations.length * 0.000001, totalOperations: operations.length, efficiency: 0.85, }; // Total costs const currentCost = storage.memoryCost + storage.diskCost + network.ingressCost + network.egressCost + compute.cpuCost; const projectedCost = currentCost * 1.1; // 10% growth const costTrend = this.calculateCostTrend(currentCost); const total = { current: currentCost, projected: projectedCost, trend: costTrend, costPerGB: currentCost / (memoryGB || 1), costPerOperation: currentCost / (operations.length || 1), }; // Projections const projections = [ { period: '1 week', estimatedCost: currentCost * 7, confidence: 0.9 }, { period: '1 month', estimatedCost: currentCost * 30, confidence: 0.7 }, { period: '3 months', estimatedCost: currentCost * 90, confidence: 0.5 }, ]; // Optimizations const optimizations = []; if (storage.utilizationPercent < 50) { optimizations.push({ category: 'Storage', potentialSavings: storage.memoryCost * 0.3, effort: 'low', recommendation: 'Reduce cache size to match actual usage', }); } if (compute.efficiency < 0.8) { optimizations.push({ category: 'Compute', potentialSavings: compute.cpuCost * 0.2, effort: 'medium', recommendation: 'Optimize cache operations to reduce CPU usage', }); } const costAnalysis = { timestamp: now, timeRange, storage, network, compute, total, projections, optimizations, }; this.emit('costs-analyzed', costAnalysis); return costAnalysis; } // ============================================================================ // Data Export // ============================================================================ /** * Export analytics data */ async exportData(options) { const format = options.format || 'json'; const now = Date.now(); const timeRange = options.timeRange || { start: now - 86400000, end: now }; // Gather all data const dashboard = await this.getDashboard({ ...options, timeRange }); const metrics = await this.getMetrics({ ...options, timeRange }); const trends = await this.analyzeTrends({ ...options, timeRange }); const alerts = await this.checkAlerts({ ...options, timeRange }); const bottlenecks = await this.identifyBottlenecks({ ...options, timeRange, }); const costs = await this.analyzeCosts({ ...options, timeRange }); const exportData = { exportTimestamp: now, timeRange, dashboard, metrics, trends, alerts, bottlenecks, costs, }; let output; switch (format) { case 'json': output = JSON.stringify(exportData, null, 2); break; case 'csv': output = this.convertToCSV(exportData); break; case 'prometheus': output = this.convertToPrometheus(exportData); break; default: throw new Error(`Unknown export format: ${format}`); } // Write to file if path provided if (options.filePath) { writeFileSync(options.filePath, output, 'utf-8'); this.emit('data-exported', { format, path: options.filePath, size: output.length, }); } return output; } /** * Convert to CSV format */ convertToCSV(data) { const lines = []; // Header lines.push('Metric,Value,Timestamp'); // Dashboard data if (data.dashboard) { const d = data.dashboard; lines.push(`Hit Rate,${d.performance.hitRate},${d.timestamp}`); lines.push(`Latency P95,${d.performance.latencyP95},${d.timestamp}`); lines.push(`Throughput,${d.performance.throughput},${d.timestamp}`); lines.push(`Total Keys,${d.usage.totalKeys},${d.timestamp}`); lines.push(`Total Size,${d.usage.totalSize},${d.timestamp}`); lines.push(`Health Score,${d.health.healthScore},${d.timestamp}`); } return lines.join('\n'); } /** * Convert to Prometheus format */ convertToPrometheus(data) { const lines = []; const timestamp = Date.now(); if (data.dashboard) { const d = data.dashboard; lines.push(`# HELP cache_hit_rate Cache hit rate percentage`, `# TYPE cache_hit_rate gauge`, `cache_hit_rate ${d.performance.hitRate} ${timestamp}`, ``, `# HELP cache_latency_p95 95th percentile latency in milliseconds`, `# TYPE cache_latency_p95 gauge`, `cache_latency_p95 ${d.performance.latencyP95} ${timestamp}`, ``, `# HELP cache_throughput Operations per second`, `# TYPE cache_throughput gauge`, `cache_throughput ${d.performance.throughput} ${timestamp}`, ``, `# HELP cache_health_score Over