/ profilers / router-performance-monitor.js
router-performance-monitor.js
  1  /**
  2   * Performance monitoring utilities for the video router
  3   */
  4  import { createLogger } from './logger.js';
  5  import { getMultipleVideoSources } from '../app/services/video/router.js';
  6  
  7  const logger = createLogger('RouterPerformance');
  8  
  9  // Performance metrics storage
 10  const metrics = {
 11    totalCalls: 0,
 12    totalTimeMs: 0,
 13    averageTimeMs: 0,
 14    minTimeMs: Infinity,
 15    maxTimeMs: 0,
 16    lastMeasurements: [],
 17    maxMeasurements: 100,
 18    byCondition: {
 19      withCidData: { count: 0, totalTimeMs: 0, averageTimeMs: 0 },
 20      withoutCidData: { count: 0, totalTimeMs: 0, averageTimeMs: 0 },
 21      withPreemptiveResults: { count: 0, totalTimeMs: 0, averageTimeMs: 0 },
 22      withoutPreemptiveResults: { count: 0, totalTimeMs: 0, averageTimeMs: 0 }
 23    }
 24  };
 25  
 26  /**
 27   * Wraps the getMultipleVideoSources function to monitor performance
 28   * @param {string} cid - Content ID
 29   * @param {number} maxSources - Maximum number of sources
 30   * @param {string} altcid - Alternative content ID
 31   * @param {Object} livestreamInfo - Livestream information
 32   * @returns {Array} Video sources
 33   */
 34  export function monitoredGetMultipleVideoSources(cid, maxSources, altcid, livestreamInfo = null) {
 35    const start = performance.now();
 36  
 37    try {
 38      // Determine if this is a livestream call
 39      const isLivestream = !!livestreamInfo;
 40  
 41      // Check if we have CID performance data
 42      const hasCidData = cid && window._routerState &&
 43                        window._routerState.cidPerformanceData &&
 44                        window._routerState.cidPerformanceData[cid] &&
 45                        Object.keys(window._routerState.cidPerformanceData[cid]).length > 0;
 46  
 47      // Check if we have preemptive test results
 48      const hasPreemptiveResults = window._routerState &&
 49                                 window._routerState.preemptiveTestResults &&
 50                                 window._routerState.preemptiveTestResults.length > 0;
 51  
 52      // Call the original function
 53      const result = getMultipleVideoSources(cid, maxSources, altcid, livestreamInfo);
 54  
 55      // Measure performance
 56      const end = performance.now();
 57      const executionTime = end - start;
 58  
 59      // Update metrics with additional context
 60      updateMetrics(executionTime, cid, hasCidData, hasPreemptiveResults);
 61  
 62      return result;
 63    } catch (error) {
 64      logger.error(`Error in getMultipleVideoSources: ${error.message}`);
 65      throw error;
 66    }
 67  }
 68  
 69  /**
 70   * Updates performance metrics
 71   * @param {number} executionTime - Execution time in milliseconds
 72   * @param {string} cid - Content ID used in the call
 73   * @param {boolean} [hasCidData=false] - Whether CID performance data was available
 74   * @param {boolean} [hasPreemptiveResults=false] - Whether preemptive test results were used
 75   * @private
 76   */
 77  function updateMetrics(executionTime, cid, hasCidData = false, hasPreemptiveResults = false) {
 78    // Update overall metrics
 79    metrics.totalCalls++;
 80    metrics.totalTimeMs += executionTime;
 81    metrics.averageTimeMs = metrics.totalTimeMs / metrics.totalCalls;
 82    metrics.minTimeMs = Math.min(metrics.minTimeMs, executionTime);
 83    metrics.maxTimeMs = Math.max(metrics.maxTimeMs, executionTime);
 84  
 85    // Determine if this is a livestream call based on CID
 86    const isLivestream = !cid || cid === '';
 87  
 88    // Add to recent measurements
 89    metrics.lastMeasurements.push({
 90      timestamp: Date.now(),
 91      executionTimeMs: executionTime,
 92      cid: isLivestream ? 'livestream' : cid.slice(0, 8),
 93      isLivestream,
 94      hasCidData,
 95      hasPreemptiveResults
 96    });
 97  
 98    // Keep only the most recent measurements
 99    if (metrics.lastMeasurements.length > metrics.maxMeasurements) {
100      metrics.lastMeasurements.shift();
101    }
102  
103    // Update condition-specific metrics
104    if (hasCidData) {
105      metrics.byCondition.withCidData.count++;
106      metrics.byCondition.withCidData.totalTimeMs += executionTime;
107      metrics.byCondition.withCidData.averageTimeMs =
108        metrics.byCondition.withCidData.totalTimeMs / metrics.byCondition.withCidData.count;
109    } else {
110      metrics.byCondition.withoutCidData.count++;
111      metrics.byCondition.withoutCidData.totalTimeMs += executionTime;
112      metrics.byCondition.withoutCidData.averageTimeMs =
113        metrics.byCondition.withoutCidData.totalTimeMs / metrics.byCondition.withoutCidData.count;
114    }
115  
116    if (hasPreemptiveResults) {
117      metrics.byCondition.withPreemptiveResults.count++;
118      metrics.byCondition.withPreemptiveResults.totalTimeMs += executionTime;
119      metrics.byCondition.withPreemptiveResults.averageTimeMs =
120        metrics.byCondition.withPreemptiveResults.totalTimeMs / metrics.byCondition.withPreemptiveResults.count;
121    } else {
122      metrics.byCondition.withoutPreemptiveResults.count++;
123      metrics.byCondition.withoutPreemptiveResults.totalTimeMs += executionTime;
124      metrics.byCondition.withoutPreemptiveResults.averageTimeMs =
125        metrics.byCondition.withoutPreemptiveResults.totalTimeMs / metrics.byCondition.withoutPreemptiveResults.count;
126    }
127  
128    // Log significant performance issues
129    if (executionTime > 100) {
130      logger.warn(`Slow getMultipleVideoSources call: ${executionTime.toFixed(2)}ms for ${isLivestream ? 'livestream' : `CID ${cid.slice(0, 8)}`}`);
131    }
132  }
133  
134  /**
135   * Gets the current performance metrics
136   * @returns {Object} Performance metrics
137   */
138  export function getRouterPerformanceMetrics() {
139    return {
140      ...metrics,
141      lastMeasurements: [...metrics.lastMeasurements]
142    };
143  }
144  
145  /**
146   * Resets the performance metrics
147   */
148  export function resetRouterPerformanceMetrics() {
149    metrics.totalCalls = 0;
150    metrics.totalTimeMs = 0;
151    metrics.averageTimeMs = 0;
152    metrics.minTimeMs = Infinity;
153    metrics.maxTimeMs = 0;
154    metrics.lastMeasurements = [];
155  
156    Object.keys(metrics.byCondition).forEach(key => {
157      metrics.byCondition[key] = { count: 0, totalTimeMs: 0, averageTimeMs: 0 };
158    });
159  
160    logger.info('Router performance metrics reset');
161  }
162  
163  /**
164   * Logs the current performance metrics
165   */
166  export function logRouterPerformanceMetrics() {
167    logger.info('Router Performance Metrics:');
168    logger.info(`Total calls: ${metrics.totalCalls}`);
169    logger.info(`Average time: ${metrics.averageTimeMs.toFixed(2)}ms`);
170    logger.info(`Min time: ${metrics.minTimeMs.toFixed(2)}ms`);
171    logger.info(`Max time: ${metrics.maxTimeMs.toFixed(2)}ms`);
172  
173    if (metrics.lastMeasurements.length > 0) {
174      const recentAvg = metrics.lastMeasurements.reduce((sum, m) => sum + m.executionTimeMs, 0) /
175                        metrics.lastMeasurements.length;
176      logger.info(`Recent average (${metrics.lastMeasurements.length} calls): ${recentAvg.toFixed(2)}ms`);
177    }
178  
179    // Log condition-specific metrics
180    logger.info('Condition-specific metrics:');
181    logger.info(`With CID data: ${metrics.byCondition.withCidData.count} calls, ` +
182                `avg: ${metrics.byCondition.withCidData.averageTimeMs.toFixed(2)}ms`);
183    logger.info(`Without CID data: ${metrics.byCondition.withoutCidData.count} calls, ` +
184                `avg: ${metrics.byCondition.withoutCidData.averageTimeMs.toFixed(2)}ms`);
185    logger.info(`With preemptive results: ${metrics.byCondition.withPreemptiveResults.count} calls, ` +
186                `avg: ${metrics.byCondition.withPreemptiveResults.averageTimeMs.toFixed(2)}ms`);
187    logger.info(`Without preemptive results: ${metrics.byCondition.withoutPreemptiveResults.count} calls, ` +
188                `avg: ${metrics.byCondition.withoutPreemptiveResults.averageTimeMs.toFixed(2)}ms`);
189  }