UNPKG

network-performance-analyzer

Version:

Automated analysis tool for network performance test datasets containing DNS testing results and iperf3 performance measurements

391 lines (338 loc) 13.9 kB
// DnsAnalyzer Service Implementation import { Dataset, DnsTestResult, DnsPerformanceMetrics, DomainPerformance, DnsServerComparison, DnsAnalysis, AnalysisError } from '../models'; import { DefaultErrorHandler } from '../utils/ErrorHandler'; /** * DnsAnalyzer class for processing DNS resolution performance metrics * Analyzes response times, success rates, and domain performance across datasets */ export class DnsAnalyzer { private errorHandler: DefaultErrorHandler; /** * Creates a new instance of DnsAnalyzer */ constructor() { this.errorHandler = new DefaultErrorHandler(); } /** * Analyze DNS performance metrics across datasets * @param datasets The datasets containing DNS test results * @returns DNS performance analysis results */ analyzeDnsPerformance(datasets: Dataset[]): DnsAnalysis { try { // Calculate performance metrics for each configuration const performanceMetrics = this.calculatePerformanceMetrics(datasets); // Calculate domain rankings across all configurations const domainRankings = this.calculateDomainRankings(datasets); // Calculate DNS server comparison metrics const serverComparison = this.calculateServerComparison(datasets); return { performanceMetrics, domainRankings, serverComparison }; } catch (error) { const analysisError = new Error(error instanceof Error ? error.message : String(error)) as AnalysisError; analysisError.analysisType = 'dns_performance'; this.errorHandler.handleAnalysisError(analysisError); // Return empty analysis in case of error return { performanceMetrics: [], domainRankings: [], serverComparison: [] }; } } /** * Calculate DNS performance metrics for each configuration * @param datasets The datasets containing DNS test results * @returns Array of DNS performance metrics for each configuration */ calculatePerformanceMetrics(datasets: Dataset[]): DnsPerformanceMetrics[] { try { return datasets.map(dataset => { const dnsResults = this.getDnsResults(dataset); // Calculate response time statistics const responseTimesMs = dnsResults .filter(result => result.success && result.responseTimeMs !== undefined) .map(result => result.responseTimeMs as number); // Calculate success rate const successRate = dnsResults.length > 0 ? dnsResults.filter(result => result.success).length / dnsResults.length : 0; // Calculate domain performance metrics const domainPerformance = this.calculateDomainPerformanceForDataset(dataset); // Sort domains by response time const sortedDomains = [...domainPerformance].sort((a, b) => b.avgResponseTimeMs - a.avgResponseTimeMs ); // Get slowest and fastest domains const slowestDomains = sortedDomains.slice(0, 5); const fastestDomains = [...sortedDomains].reverse().slice(0, 5); return { configuration: dataset.name, avgResponseTimeMs: this.calculateMean(responseTimesMs), medianResponseTimeMs: this.calculateMedian(responseTimesMs), successRate, slowestDomains, fastestDomains }; }); } catch (error) { const analysisError = new Error(error instanceof Error ? error.message : String(error)) as AnalysisError; analysisError.analysisType = 'dns_performance_metrics'; this.errorHandler.handleAnalysisError(analysisError); return []; } } /** * Calculate domain performance rankings across all datasets * @param datasets The datasets containing DNS test results * @returns Array of domain performance metrics sorted by average response time */ calculateDomainRankings(datasets: Dataset[]): DomainPerformance[] { try { // Get all unique domains across all datasets const allDomains = new Set<string>(); datasets.forEach(dataset => { const dnsResults = this.getDnsResults(dataset); dnsResults.forEach(result => { if (result.domain) { allDomains.add(result.domain); } }); }); // Calculate performance metrics for each domain across all datasets const domainPerformance: DomainPerformance[] = Array.from(allDomains).map(domain => { let totalResponseTime = 0; let successCount = 0; let totalCount = 0; // Collect metrics for this domain across all datasets datasets.forEach(dataset => { const dnsResults = this.getDnsResults(dataset); const domainResults = dnsResults.filter(result => result.domain === domain); domainResults.forEach(result => { totalCount++; if (result.success && result.responseTimeMs !== undefined) { totalResponseTime += result.responseTimeMs; successCount++; } }); }); // Calculate average response time and success rate const avgResponseTimeMs = successCount > 0 ? totalResponseTime / successCount : 0; const successRate = totalCount > 0 ? successCount / totalCount : 0; return { domain, avgResponseTimeMs, successRate, queryCount: totalCount }; }); // Sort domains by average response time (slowest first) return domainPerformance.sort((a, b) => b.avgResponseTimeMs - a.avgResponseTimeMs); } catch (error) { const analysisError = new Error(error instanceof Error ? error.message : String(error)) as AnalysisError; analysisError.analysisType = 'domain_rankings'; this.errorHandler.handleAnalysisError(analysisError); return []; } } /** * Calculate DNS server comparison metrics across all datasets * @param datasets The datasets containing DNS test results * @returns Array of DNS server comparison metrics */ calculateServerComparison(datasets: Dataset[]): DnsServerComparison[] { try { // Get all unique DNS servers across all datasets const allServers = new Set<string>(); datasets.forEach(dataset => { const dnsResults = this.getDnsResults(dataset); dnsResults.forEach(result => { if (result.dnsServer) { allServers.add(result.dnsServer); } }); }); // Calculate performance metrics for each DNS server across all datasets const serverComparison: DnsServerComparison[] = Array.from(allServers).map(server => { let totalResponseTime = 0; let successCount = 0; let totalCount = 0; const configurations = new Set<string>(); // Collect metrics for this server across all datasets datasets.forEach(dataset => { const dnsResults = this.getDnsResults(dataset); const serverResults = dnsResults.filter(result => result.dnsServer === server); if (serverResults.length > 0) { configurations.add(dataset.name); } serverResults.forEach(result => { totalCount++; if (result.success && result.responseTimeMs !== undefined) { totalResponseTime += result.responseTimeMs; successCount++; } }); }); // Calculate average response time and success rate const avgResponseTimeMs = successCount > 0 ? totalResponseTime / successCount : 0; const successRate = totalCount > 0 ? successCount / totalCount : 0; return { server, avgResponseTimeMs, successRate, configurations: Array.from(configurations) }; }); // Sort servers by average response time (fastest first) return serverComparison.sort((a, b) => a.avgResponseTimeMs - b.avgResponseTimeMs); } catch (error) { const analysisError = new Error(error instanceof Error ? error.message : String(error)) as AnalysisError; analysisError.analysisType = 'server_comparison'; this.errorHandler.handleAnalysisError(analysisError); return []; } } /** * Calculate domain performance metrics for a single dataset * @param dataset The dataset containing DNS test results * @returns Array of domain performance metrics */ private calculateDomainPerformanceForDataset(dataset: Dataset): DomainPerformance[] { try { const dnsResults = this.getDnsResults(dataset); // Get all unique domains in this dataset const domains = new Set<string>(); dnsResults.forEach(result => { if (result.domain) { domains.add(result.domain); } }); // Calculate performance metrics for each domain return Array.from(domains).map(domain => { const domainResults = dnsResults.filter(result => result.domain === domain); const successfulResults = domainResults.filter(result => result.success && result.responseTimeMs !== undefined ); const responseTimes = successfulResults.map(result => result.responseTimeMs as number); const avgResponseTimeMs = this.calculateMean(responseTimes); const successRate = domainResults.length > 0 ? successfulResults.length / domainResults.length : 0; return { domain, avgResponseTimeMs, successRate, queryCount: domainResults.length }; }); } catch (error) { const analysisError = new Error(error instanceof Error ? error.message : String(error)) as AnalysisError; analysisError.analysisType = 'domain_performance'; analysisError.datasetName = dataset.name; this.errorHandler.handleAnalysisError(analysisError); return []; } } /** * Analyze DNS failure patterns across datasets * @param datasets The datasets containing DNS test results * @returns Object containing failure pattern analysis */ analyzeFailurePatterns(datasets: Dataset[]): Record<string, number> { try { // Initialize failure pattern counter const failurePatterns: Record<string, number> = {}; // Count occurrences of each failure pattern datasets.forEach(dataset => { const dnsResults = this.getDnsResults(dataset); dnsResults.forEach(result => { if (!result.success && result.error) { // Normalize error message to create a pattern const pattern = this.normalizeErrorMessage(result.error); // Increment pattern count failurePatterns[pattern] = (failurePatterns[pattern] || 0) + 1; } }); }); return failurePatterns; } catch (error) { const analysisError = new Error(error instanceof Error ? error.message : String(error)) as AnalysisError; analysisError.analysisType = 'failure_patterns'; this.errorHandler.handleAnalysisError(analysisError); return {}; } } /** * Normalize error message to create a pattern * @param errorMessage The error message to normalize * @returns Normalized error pattern */ private normalizeErrorMessage(errorMessage: string): string { // Remove specific details like IP addresses, timestamps, etc. // This helps group similar errors together return errorMessage .replace(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/g, '[IP]') .replace(/\b\d+\b/g, '[NUM]') .replace(/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi, '[UUID]') .replace(/\b[0-9a-f]{24}\b/gi, '[ID]') .trim(); } /** * Get DNS test results from a dataset * @param dataset The dataset to extract DNS results from * @returns Array of DNS test results */ private getDnsResults(dataset: Dataset): DnsTestResult[] { try { // This is a placeholder - in a real implementation, we would need to load the results file // For now, we'll assume the dataset already has the parsed results return (dataset as any).results?.dnsResults || []; } catch (error) { const analysisError = new Error(error instanceof Error ? error.message : String(error)) as AnalysisError; analysisError.analysisType = 'data_extraction'; analysisError.datasetName = dataset.name; this.errorHandler.handleAnalysisError(analysisError); return []; } } /** * Calculate the mean of an array of numbers * @param values Array of numbers * @returns The mean value, or 0 if the array is empty */ private calculateMean(values: number[]): number { if (values.length === 0) return 0; const sum = values.reduce((acc, val) => acc + val, 0); return sum / values.length; } /** * Calculate the median of an array of numbers * @param values Array of numbers * @returns The median value, or 0 if the array is empty */ private calculateMedian(values: number[]): number { if (values.length === 0) return 0; const sortedValues = [...values].sort((a, b) => a - b); const mid = Math.floor(sortedValues.length / 2); if (sortedValues.length % 2 === 0) { // For even-length arrays, average the two middle values const midValue1 = sortedValues[mid - 1] || 0; const midValue2 = sortedValues[mid] || 0; return (midValue1 + midValue2) / 2; } else { // For odd-length arrays, return the middle value return sortedValues[mid] || 0; } } }