network-performance-analyzer
Version:
Automated analysis tool for network performance test datasets containing DNS testing results and iperf3 performance measurements
605 lines (511 loc) • 21.5 kB
text/typescript
// ConfigurationComparator Service Implementation
import {
Dataset,
ConfigurationComparison,
MtuAnalysis,
LoggingAnalysis,
ConfigurationRanking,
PerformanceSummary,
BandwidthMetrics,
LatencyMetrics,
ReliabilityMetrics,
CpuMetrics,
DnsPerformanceMetrics,
AnalysisError
} from '../models';
import { DefaultErrorHandler } from '../utils/ErrorHandler';
import { IperfAnalyzer } from './IperfAnalyzer';
import { DnsAnalyzer } from './DnsAnalyzer';
/**
* ConfigurationComparator class for analyzing performance differences between configurations
* Compares MTU settings, AWS logging impact, and ranks configurations by performance
*/
export class ConfigurationComparator {
private errorHandler: DefaultErrorHandler;
private iperfAnalyzer: IperfAnalyzer;
private dnsAnalyzer: DnsAnalyzer;
/**
* Creates a new instance of ConfigurationComparator
*/
constructor() {
this.errorHandler = new DefaultErrorHandler();
this.iperfAnalyzer = new IperfAnalyzer();
this.dnsAnalyzer = new DnsAnalyzer();
}
/**
* Compare different network configurations based on performance metrics
* @param datasets The datasets representing different network configurations
* @returns Configuration comparison analysis
*/
compareConfigurations(datasets: Dataset[]): ConfigurationComparison {
try {
// Analyze MTU impact
const mtuImpact = this.analyzeMtuImpact(datasets);
// Analyze AWS logging impact
const loggingImpact = this.analyzeLoggingImpact(datasets);
// Create overall configuration ranking
const overallRanking = this.rankConfigurations(datasets);
return {
mtuImpact,
loggingImpact,
overallRanking
};
} catch (error) {
const analysisError = new Error(error instanceof Error ? error.message : String(error)) as AnalysisError;
analysisError.analysisType = 'configuration_comparison';
this.errorHandler.handleAnalysisError(analysisError);
// Return empty analysis in case of error
return {
mtuImpact: {
optimalMtu: 0,
performanceByMtu: {},
recommendations: []
},
loggingImpact: {
performanceImpact: 0,
bandwidthDifference: 0,
latencyDifference: 0,
recommendations: []
},
overallRanking: []
};
}
}
/**
* Analyze the impact of different MTU settings on performance
* @param datasets The datasets with different MTU configurations
* @returns MTU impact analysis
*/
analyzeMtuImpact(datasets: Dataset[]): MtuAnalysis {
try {
// Group datasets by MTU
const datasetsByMtu = this.groupDatasetsByMtu(datasets);
// Calculate performance metrics for each MTU
const performanceByMtu: { [mtu: number]: PerformanceSummary } = {};
let optimalMtu = 0;
let bestBandwidth = 0;
// Process each MTU group
for (const [mtuStr, mtuDatasets] of Object.entries(datasetsByMtu)) {
const mtu = parseInt(mtuStr, 10);
// Calculate bandwidth metrics
const bandwidthMetrics = this.iperfAnalyzer.analyzeBandwidth(mtuDatasets);
const avgBandwidth = this.calculateAverageMetric(bandwidthMetrics, 'avgBandwidthMbps');
// Calculate latency metrics
const latencyMetrics = this.iperfAnalyzer.analyzeLatency(mtuDatasets);
const avgLatency = this.calculateAverageMetric(latencyMetrics, 'avgLatencyMs');
// Calculate reliability metrics
const reliabilityMetrics = this.iperfAnalyzer.analyzeReliability(mtuDatasets);
const successRate = this.calculateAverageMetric(reliabilityMetrics, 'successRate');
// Calculate CPU usage metrics
const cpuMetrics = this.iperfAnalyzer.analyzeCpuUtilization(mtuDatasets);
const cpuUsage = this.calculateAverageMetric(cpuMetrics, 'avgHostCpuUsage');
// Store performance summary for this MTU
performanceByMtu[mtu] = {
avgBandwidth,
avgLatency,
successRate,
cpuUsage
};
// Track optimal MTU based on bandwidth
if (avgBandwidth > bestBandwidth) {
bestBandwidth = avgBandwidth;
optimalMtu = mtu;
}
}
// Generate recommendations based on MTU analysis
const recommendations = this.generateMtuRecommendations(performanceByMtu, optimalMtu);
return {
optimalMtu,
performanceByMtu,
recommendations
};
} catch (error) {
const analysisError = new Error(error instanceof Error ? error.message : String(error)) as AnalysisError;
analysisError.analysisType = 'mtu_impact';
this.errorHandler.handleAnalysisError(analysisError);
// Return empty analysis in case of error
return {
optimalMtu: 0,
performanceByMtu: {},
recommendations: []
};
}
}
/**
* Analyze the impact of AWS logging on performance
* @param datasets The datasets with different AWS logging configurations
* @returns AWS logging impact analysis
*/
analyzeLoggingImpact(datasets: Dataset[]): LoggingAnalysis {
try {
// Group datasets by AWS logging status
const loggingEnabled = datasets.filter(dataset => dataset.configuration.awsLogging);
const loggingDisabled = datasets.filter(dataset => !dataset.configuration.awsLogging);
// If we don't have both enabled and disabled datasets, return empty analysis
if (loggingEnabled.length === 0 || loggingDisabled.length === 0) {
return {
performanceImpact: 0,
bandwidthDifference: 0,
latencyDifference: 0,
recommendations: [
"Insufficient data to analyze AWS logging impact. Need datasets with both enabled and disabled logging."
]
};
}
// Calculate bandwidth metrics for both groups
const enabledBandwidth = this.iperfAnalyzer.analyzeBandwidth(loggingEnabled);
const disabledBandwidth = this.iperfAnalyzer.analyzeBandwidth(loggingDisabled);
const avgEnabledBandwidth = this.calculateAverageMetric(enabledBandwidth, 'avgBandwidthMbps');
const avgDisabledBandwidth = this.calculateAverageMetric(disabledBandwidth, 'avgBandwidthMbps');
// Calculate bandwidth difference (positive means disabled is better)
const bandwidthDifference = avgDisabledBandwidth - avgEnabledBandwidth;
// Calculate latency metrics for both groups
const enabledLatency = this.iperfAnalyzer.analyzeLatency(loggingEnabled);
const disabledLatency = this.iperfAnalyzer.analyzeLatency(loggingDisabled);
const avgEnabledLatency = this.calculateAverageMetric(enabledLatency, 'avgLatencyMs');
const avgDisabledLatency = this.calculateAverageMetric(disabledLatency, 'avgLatencyMs');
// Calculate latency difference (positive means enabled is worse)
const latencyDifference = avgEnabledLatency - avgDisabledLatency;
// Calculate overall performance impact as a percentage
// We'll use bandwidth as the primary metric for overall impact
const performanceImpact = avgDisabledBandwidth > 0
? (bandwidthDifference / avgDisabledBandwidth) * 100
: 0;
// Generate recommendations based on logging impact
const recommendations = this.generateLoggingRecommendations(
performanceImpact,
bandwidthDifference,
latencyDifference
);
return {
performanceImpact,
bandwidthDifference,
latencyDifference,
recommendations
};
} catch (error) {
const analysisError = new Error(error instanceof Error ? error.message : String(error)) as AnalysisError;
analysisError.analysisType = 'logging_impact';
this.errorHandler.handleAnalysisError(analysisError);
// Return empty analysis in case of error
return {
performanceImpact: 0,
bandwidthDifference: 0,
latencyDifference: 0,
recommendations: []
};
}
}
/**
* Rank configurations based on overall performance
* @param datasets The datasets representing different configurations
* @returns Array of configuration rankings
*/
rankConfigurations(datasets: Dataset[]): ConfigurationRanking[] {
try {
const rankings: ConfigurationRanking[] = [];
// Calculate scores for each dataset
for (const dataset of datasets) {
// Get bandwidth metrics
const bandwidthMetrics = this.iperfAnalyzer.analyzeBandwidth([dataset]);
const bandwidthScore = bandwidthMetrics.length > 0 ? bandwidthMetrics[0]?.avgBandwidthMbps || 0 : 0;
// Get latency metrics (lower is better, so we'll invert for scoring)
const latencyMetrics = this.iperfAnalyzer.analyzeLatency([dataset]);
const latencyValue = latencyMetrics.length > 0 ? latencyMetrics[0]?.avgLatencyMs || 0 : 0;
// Convert latency to a score where higher is better
const latencyScore = latencyValue > 0 ? 100 / latencyValue : 0;
// Get reliability metrics
const reliabilityMetrics = this.iperfAnalyzer.analyzeReliability([dataset]);
const reliabilityScore = reliabilityMetrics.length > 0 ? (reliabilityMetrics[0]?.successRate || 0) * 100 : 0;
// Calculate overall score (weighted average)
const overallScore = (
(bandwidthScore * 0.5) + // 50% weight to bandwidth
(latencyScore * 0.3) + // 30% weight to latency
(reliabilityScore * 0.2) // 20% weight to reliability
);
rankings.push({
configuration: dataset.name,
overallScore,
bandwidthScore,
latencyScore,
reliabilityScore,
rank: 0 // Will be set after sorting
});
}
// Sort rankings by overall score (descending)
rankings.sort((a, b) => b.overallScore - a.overallScore);
// Assign ranks
rankings.forEach((ranking, index) => {
ranking.rank = index + 1;
});
return rankings;
} catch (error) {
const analysisError = new Error(error instanceof Error ? error.message : String(error)) as AnalysisError;
analysisError.analysisType = 'configuration_ranking';
this.errorHandler.handleAnalysisError(analysisError);
// Return empty rankings in case of error
return [];
}
}
/**
* Analyze performance trends across configurations
* @param datasets The datasets to analyze for trends
* @returns Object containing trend analysis
*/
analyzePerformanceTrends(datasets: Dataset[]): Record<string, any> {
try {
// Group datasets by MTU
const datasetsByMtu = this.groupDatasetsByMtu(datasets);
// Group datasets by backend server
const datasetsByServer = this.groupDatasetsByServer(datasets);
// Group datasets by AWS logging status
const datasetsByLogging = this.groupDatasetsByLogging(datasets);
// Analyze trends for each grouping
const mtuTrends = this.analyzeTrendsByGroup(datasetsByMtu, 'MTU');
const serverTrends = this.analyzeTrendsByGroup(datasetsByServer, 'Server');
const loggingTrends = this.analyzeTrendsByGroup(datasetsByLogging, 'Logging');
return {
mtuTrends,
serverTrends,
loggingTrends
};
} catch (error) {
const analysisError = new Error(error instanceof Error ? error.message : String(error)) as AnalysisError;
analysisError.analysisType = 'performance_trends';
this.errorHandler.handleAnalysisError(analysisError);
// Return empty analysis in case of error
return {};
}
}
/**
* Group datasets by MTU setting
* @param datasets The datasets to group
* @returns Object with MTU values as keys and arrays of datasets as values
*/
private groupDatasetsByMtu(datasets: Dataset[]): Record<number, Dataset[]> {
const groups: Record<number, Dataset[]> = {};
for (const dataset of datasets) {
const mtu = dataset.configuration.mtu;
if (!groups[mtu]) {
groups[mtu] = [];
}
groups[mtu].push(dataset);
}
return groups;
}
/**
* Group datasets by backend server
* @param datasets The datasets to group
* @returns Object with server names as keys and arrays of datasets as values
*/
private groupDatasetsByServer(datasets: Dataset[]): Record<string, Dataset[]> {
const groups: Record<string, Dataset[]> = {};
for (const dataset of datasets) {
const server = dataset.configuration.backendServer;
if (!groups[server]) {
groups[server] = [];
}
groups[server].push(dataset);
}
return groups;
}
/**
* Group datasets by AWS logging status
* @param datasets The datasets to group
* @returns Object with logging status as keys and arrays of datasets as values
*/
private groupDatasetsByLogging(datasets: Dataset[]): Record<string, Dataset[]> {
const groups: Record<string, Dataset[]> = {};
for (const dataset of datasets) {
const loggingStatus = dataset.configuration.awsLogging ? 'enabled' : 'disabled';
if (!groups[loggingStatus]) {
groups[loggingStatus] = [];
}
groups[loggingStatus].push(dataset);
}
return groups;
}
/**
* Analyze performance trends for a group of datasets
* @param groupedDatasets The grouped datasets to analyze
* @param groupType The type of grouping (e.g., 'MTU', 'Server', 'Logging')
* @returns Object containing trend analysis for the group
*/
private analyzeTrendsByGroup(groupedDatasets: Record<string, Dataset[]>, groupType: string): Record<string, any> {
const trends: Record<string, any> = {};
for (const [groupKey, datasets] of Object.entries(groupedDatasets)) {
// Calculate bandwidth metrics
const bandwidthMetrics = this.iperfAnalyzer.analyzeBandwidth(datasets);
const avgBandwidth = this.calculateAverageMetric(bandwidthMetrics, 'avgBandwidthMbps');
// Calculate latency metrics
const latencyMetrics = this.iperfAnalyzer.analyzeLatency(datasets);
const avgLatency = this.calculateAverageMetric(latencyMetrics, 'avgLatencyMs');
// Calculate reliability metrics
const reliabilityMetrics = this.iperfAnalyzer.analyzeReliability(datasets);
const successRate = this.calculateAverageMetric(reliabilityMetrics, 'successRate');
// Calculate DNS performance metrics
const dnsAnalysis = this.dnsAnalyzer.analyzeDnsPerformance(datasets);
const avgDnsResponseTime = this.calculateAverageMetric(
dnsAnalysis.performanceMetrics,
'avgResponseTimeMs'
);
trends[groupKey] = {
avgBandwidth,
avgLatency,
successRate,
avgDnsResponseTime,
datasetCount: datasets.length
};
}
return trends;
}
/**
* Calculate the average of a specific metric across an array of objects
* @param metrics Array of objects containing the metric
* @param metricKey The key of the metric to average
* @returns The average value of the metric
*/
private calculateAverageMetric<T>(metrics: T[], metricKey: keyof T): number {
if (metrics.length === 0) return 0;
let sum = 0;
let count = 0;
for (const metric of metrics) {
const value = metric[metricKey];
if (typeof value === 'number' && !isNaN(value)) {
sum += value;
count++;
}
}
return count > 0 ? sum / count : 0;
}
/**
* Generate recommendations based on MTU analysis
* @param performanceByMtu Performance metrics for each MTU
* @param optimalMtu The optimal MTU value
* @returns Array of recommendation strings
*/
private generateMtuRecommendations(
performanceByMtu: { [mtu: number]: PerformanceSummary },
optimalMtu: number
): string[] {
const recommendations: string[] = [];
// If we have no data, return a generic recommendation
if (Object.keys(performanceByMtu).length === 0) {
return ["Insufficient data to make MTU recommendations."];
}
// Add recommendation for optimal MTU
recommendations.push(`The optimal MTU setting appears to be ${optimalMtu} based on bandwidth performance.`);
// Compare optimal MTU with standard MTU (1500)
if (performanceByMtu[1500] && optimalMtu !== 1500) {
const optimalPerf = performanceByMtu[optimalMtu];
const standardPerf = performanceByMtu[1500];
if (optimalPerf && standardPerf) {
const bandwidthDiff = optimalPerf.avgBandwidth - standardPerf.avgBandwidth;
const bandwidthPercent = standardPerf.avgBandwidth > 0
? (bandwidthDiff / standardPerf.avgBandwidth) * 100
: 0;
if (bandwidthPercent >= 5) {
recommendations.push(
`Using MTU ${optimalMtu} provides a ${bandwidthPercent.toFixed(1)}% bandwidth improvement over standard MTU 1500.`
);
}
}
}
// Check for jumbo frames benefit
const jumboMtus = Object.keys(performanceByMtu)
.map(mtu => parseInt(mtu, 10))
.filter(mtu => mtu > 1500);
if (jumboMtus.length > 0) {
const bestJumboMtu = Math.max(...jumboMtus);
const jumboPerf = performanceByMtu[bestJumboMtu];
if (performanceByMtu[1500] && jumboPerf) {
const standardPerf = performanceByMtu[1500];
const bandwidthDiff = jumboPerf.avgBandwidth - standardPerf.avgBandwidth;
const bandwidthPercent = standardPerf.avgBandwidth > 0
? (bandwidthDiff / standardPerf.avgBandwidth) * 100
: 0;
if (bandwidthPercent >= 10) {
recommendations.push(
`Jumbo frames (MTU ${bestJumboMtu}) provide significant bandwidth benefits (${bandwidthPercent.toFixed(1)}% improvement).`
);
} else if (bandwidthPercent <= -5) {
recommendations.push(
`Jumbo frames (MTU ${bestJumboMtu}) show worse performance than standard MTU 1500. Check network path for MTU black holes.`
);
}
}
}
// Check for small MTU performance
const smallMtus = Object.keys(performanceByMtu)
.map(mtu => parseInt(mtu, 10))
.filter(mtu => mtu < 1500);
if (smallMtus.length > 0) {
const smallestMtu = Math.min(...smallMtus);
const smallPerf = performanceByMtu[smallestMtu];
if (performanceByMtu[1500] && smallPerf) {
const standardPerf = performanceByMtu[1500];
const latencyDiff = standardPerf.avgLatency - smallPerf.avgLatency;
const latencyPercent = standardPerf.avgLatency > 0
? (latencyDiff / standardPerf.avgLatency) * 100
: 0;
if (latencyPercent >= 10) {
recommendations.push(
`Smaller MTU (${smallestMtu}) shows lower latency (${latencyPercent.toFixed(1)}% improvement) which may benefit real-time applications.`
);
}
}
}
return recommendations;
}
/**
* Generate recommendations based on AWS logging impact analysis
* @param performanceImpact Overall performance impact percentage
* @param bandwidthDifference Bandwidth difference between logging disabled and enabled
* @param latencyDifference Latency difference between logging enabled and disabled
* @returns Array of recommendation strings
*/
private generateLoggingRecommendations(
performanceImpact: number,
bandwidthDifference: number,
latencyDifference: number
): string[] {
const recommendations: string[] = [];
// Determine if logging has a significant impact
if (Math.abs(performanceImpact) < 1) {
recommendations.push(
"AWS logging has minimal impact on network performance (less than 1% difference)."
);
} else if (performanceImpact >= 1) {
// Positive impact means disabled logging performs better
recommendations.push(
`Disabling AWS logging improves network performance by approximately ${performanceImpact.toFixed(1)}%.`
);
if (performanceImpact >= 5) {
recommendations.push(
"Consider disabling AWS logging in performance-critical environments."
);
}
} else {
// Negative impact means enabled logging performs better (unusual)
recommendations.push(
`Enabling AWS logging appears to improve network performance by approximately ${Math.abs(performanceImpact).toFixed(1)}%.`
);
recommendations.push(
"This is unexpected and may indicate other factors affecting the test results."
);
}
// Add specific bandwidth recommendations
if (bandwidthDifference >= 50) {
recommendations.push(
`Disabling AWS logging provides ${bandwidthDifference.toFixed(1)} Mbps higher bandwidth.`
);
}
// Add specific latency recommendations
if (latencyDifference >= 5) {
recommendations.push(
`Enabling AWS logging increases latency by ${latencyDifference.toFixed(1)} ms.`
);
}
return recommendations;
}
}