From b49f47442f8af3231ce36b6fa17e244e788ef6b1 Mon Sep 17 00:00:00 2001 From: Saad Khan Date: Thu, 2 May 2024 16:51:08 +0530 Subject: [PATCH 01/11] update metric calculation logic to include avg, min and max Signed-off-by: Saad Khan --- .../autotune/analyzer/plots/PlotManager.java | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/autotune/analyzer/plots/PlotManager.java b/src/main/java/com/autotune/analyzer/plots/PlotManager.java index 2b3d867c1..14ec55209 100644 --- a/src/main/java/com/autotune/analyzer/plots/PlotManager.java +++ b/src/main/java/com/autotune/analyzer/plots/PlotManager.java @@ -13,6 +13,7 @@ import java.util.stream.Collectors; import static com.autotune.analyzer.recommendations.RecommendationConstants.RecommendationEngine.PercentileConstants.*; +import static com.autotune.analyzer.recommendations.RecommendationConstants.RecommendationValueConstants.CPU_ONE_CORE; public class PlotManager { private static final Logger LOGGER = LoggerFactory.getLogger(PlotManager.class); @@ -68,27 +69,46 @@ public PlotData.PlotsData generatePlots() { } PlotData.UsageData getUsageData(Map resultInRange, AnalyzerConstants.MetricName metricName, String format) { - // Extract CPU values - List cpuValues = resultInRange.values().stream() + // Extract metric values + List metricValues = resultInRange.values().stream() .filter(intervalResults -> intervalResults.getMetricResultsMap().containsKey(metricName)) - .mapToDouble(intervalResults -> { + .map(intervalResults -> { MetricResults metricResults = intervalResults.getMetricResultsMap().get(metricName); - return (metricResults != null && metricResults.getAggregationInfoResult() != null) ? metricResults.getAggregationInfoResult().getSum() : 0.0; + double metricUsageAvg = (metricResults != null) ? metricResults.getAggregationInfoResult().getAvg() : 0.0; + double metricUsageMax = (metricResults != null) ? metricResults.getAggregationInfoResult().getMax() : 0.0; + double metricUsageSum = (metricResults != null) ? metricResults.getAggregationInfoResult().getSum() : 0.0; + + double metricUsage = (metricUsageMax > 0) ? metricUsageMax : metricUsageAvg; + + double metricRequestInterval; + double cpuUsagePod = 0; + int numPods; + + if (CPU_ONE_CORE > metricUsage) { + metricRequestInterval = metricUsage; + } else { + if (metricUsageAvg != 0) { + numPods = (int) Math.ceil(metricUsageSum / metricUsageAvg); + cpuUsagePod = (numPods > 0) ? metricUsageSum / numPods : 0.0; + } + metricRequestInterval = Math.max(cpuUsagePod, metricUsage); + } + return metricRequestInterval; }) - .boxed() // Convert double to Double .collect(Collectors.toList()); - if (cpuValues.size() > 0) { - double q1 = CommonUtils.percentile(TWENTYFIVE_PERCENTILE, cpuValues); - double q3 = CommonUtils.percentile(SEVENTYFIVE_PERCENTILE, cpuValues); - double median = CommonUtils.percentile(FIFTY_PERCENTILE, cpuValues); + LOGGER.debug("metricValues : {}", metricValues); + if (!metricValues.isEmpty()) { + double q1 = CommonUtils.percentile(TWENTYFIVE_PERCENTILE, metricValues); + double q3 = CommonUtils.percentile(SEVENTYFIVE_PERCENTILE, metricValues); + double median = CommonUtils.percentile(FIFTY_PERCENTILE, metricValues); // Find max and min - double max = Collections.max(cpuValues); - double min = Collections.min(cpuValues); + double max = Collections.max(metricValues); + double min = Collections.min(metricValues); + LOGGER.debug("q1 : {}, q3 : {}, median : {}, max : {}, min : {}", q1, q3, median, max, min); return new PlotData.UsageData(min, q1, median, q3, max, format); } else { return null; } - } } From 2b658b0284adc84c063e9acfcab61e70d3683896 Mon Sep 17 00:00:00 2001 From: Saad Khan Date: Fri, 3 May 2024 01:23:15 +0530 Subject: [PATCH 02/11] fix issue when box plots are getting generated even with no recommendations Signed-off-by: Saad Khan --- .../recommendations/engine/RecommendationEngine.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/autotune/analyzer/recommendations/engine/RecommendationEngine.java b/src/main/java/com/autotune/analyzer/recommendations/engine/RecommendationEngine.java index 56da2875f..42ebbf6db 100644 --- a/src/main/java/com/autotune/analyzer/recommendations/engine/RecommendationEngine.java +++ b/src/main/java/com/autotune/analyzer/recommendations/engine/RecommendationEngine.java @@ -586,13 +586,14 @@ private boolean generateRecommendationsBasedOnTerms(ContainerData containerData, mappedRecommendationForTerm.addNotification(recommendationNotification); } mappedRecommendationForTerm.setMonitoringStartTime(monitoringStartTime); - } - Terms.setDurationBasedOnTerm(containerData, mappedRecommendationForTerm, recommendationTerm); - if (KruizeDeploymentInfo.plots == true) { - if (null != monitoringStartTime) { - mappedRecommendationForTerm.setPlots(new PlotManager(containerData.getResults(), terms, monitoringStartTime, monitoringEndTime).generatePlots()); + // generate plots when minimum data is available for the term + if (KruizeDeploymentInfo.plots) { + if (null != monitoringStartTime) { + mappedRecommendationForTerm.setPlots(new PlotManager(containerData.getResults(), terms, monitoringStartTime, monitoringEndTime).generatePlots()); + } } } + Terms.setDurationBasedOnTerm(containerData, mappedRecommendationForTerm, recommendationTerm); timestampRecommendation.setRecommendationForTermHashMap(recommendationTerm, mappedRecommendationForTerm); } From e5c92e065a7832eb571ed0a8caf343a74440e5b3 Mon Sep 17 00:00:00 2001 From: Saad Khan Date: Fri, 3 May 2024 12:16:14 +0530 Subject: [PATCH 03/11] fix issue with hardcoded format and missing format in case of zero values Signed-off-by: Saad Khan --- .../autotune/analyzer/plots/PlotManager.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/autotune/analyzer/plots/PlotManager.java b/src/main/java/com/autotune/analyzer/plots/PlotManager.java index 14ec55209..b5673b00b 100644 --- a/src/main/java/com/autotune/analyzer/plots/PlotManager.java +++ b/src/main/java/com/autotune/analyzer/plots/PlotManager.java @@ -59,8 +59,10 @@ public PlotData.PlotsData generatePlots() { calendar.add(Calendar.MILLISECOND, (int) millisecondsToAdd); // Convert the modified Calendar back to a Timestamp Timestamp newTimestamp = new Timestamp(calendar.getTimeInMillis()); - PlotData.UsageData cpuUsage = getUsageData(sortedResultsHashMap.subMap(newTimestamp, true, incrementStartTime, true), AnalyzerConstants.MetricName.cpuUsage, "cores"); - PlotData.UsageData memoryUsage = getUsageData(sortedResultsHashMap.subMap(newTimestamp, true, incrementStartTime, true), AnalyzerConstants.MetricName.memoryUsage, "MiB"); + PlotData.UsageData cpuUsage = getUsageData(sortedResultsHashMap.subMap(newTimestamp, true, incrementStartTime, + true), AnalyzerConstants.MetricName.cpuUsage); + PlotData.UsageData memoryUsage = getUsageData(sortedResultsHashMap.subMap(newTimestamp, true, + incrementStartTime, true), AnalyzerConstants.MetricName.memoryUsage); plotsDataMap.put(newTimestamp, new PlotData.PlotPoint(cpuUsage, memoryUsage)); incrementStartTime = newTimestamp; } @@ -68,7 +70,16 @@ public PlotData.PlotsData generatePlots() { return new PlotData.PlotsData(recommendationTerm.getPlots_datapoints(), plotsDataMap); } - PlotData.UsageData getUsageData(Map resultInRange, AnalyzerConstants.MetricName metricName, String format) { + PlotData.UsageData getUsageData(Map resultInRange, AnalyzerConstants.MetricName metricName) { + // Extract the format value + String format = resultInRange.values().stream() + .filter(intervalResults -> intervalResults.getMetricResultsMap().containsKey(metricName)) + .map(intervalResults -> intervalResults.getMetricResultsMap().get(metricName)) + .filter(Objects::nonNull) + .map(metricResults -> metricResults.getAggregationInfoResult().getFormat()) + .findFirst() + .orElse(null); + // Extract metric values List metricValues = resultInRange.values().stream() .filter(intervalResults -> intervalResults.getMetricResultsMap().containsKey(metricName)) @@ -97,6 +108,7 @@ PlotData.UsageData getUsageData(Map resultInRange, A }) .collect(Collectors.toList()); LOGGER.debug("metricValues : {}", metricValues); + LOGGER.debug("format : {}", format); if (!metricValues.isEmpty()) { double q1 = CommonUtils.percentile(TWENTYFIVE_PERCENTILE, metricValues); double q3 = CommonUtils.percentile(SEVENTYFIVE_PERCENTILE, metricValues); From b6adfbb3587b8abff1cbd086f267527ba0e6d214 Mon Sep 17 00:00:00 2001 From: Saad Khan Date: Fri, 3 May 2024 15:01:01 +0530 Subject: [PATCH 04/11] fix NPE in case of missing Avg, Max or Sum and other minor changes Signed-off-by: Saad Khan --- .../autotune/analyzer/plots/PlotManager.java | 60 ++++++++++++------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/autotune/analyzer/plots/PlotManager.java b/src/main/java/com/autotune/analyzer/plots/PlotManager.java index b5673b00b..f7772e73c 100644 --- a/src/main/java/com/autotune/analyzer/plots/PlotManager.java +++ b/src/main/java/com/autotune/analyzer/plots/PlotManager.java @@ -2,6 +2,7 @@ import com.autotune.analyzer.recommendations.term.Terms; import com.autotune.analyzer.utils.AnalyzerConstants; +import com.autotune.common.data.metrics.MetricAggregationInfoResults; import com.autotune.common.data.metrics.MetricResults; import com.autotune.common.data.result.IntervalResults; import com.autotune.common.utils.CommonUtils; @@ -59,10 +60,10 @@ public PlotData.PlotsData generatePlots() { calendar.add(Calendar.MILLISECOND, (int) millisecondsToAdd); // Convert the modified Calendar back to a Timestamp Timestamp newTimestamp = new Timestamp(calendar.getTimeInMillis()); - PlotData.UsageData cpuUsage = getUsageData(sortedResultsHashMap.subMap(newTimestamp, true, incrementStartTime, - true), AnalyzerConstants.MetricName.cpuUsage); + PlotData.UsageData cpuUsage = getUsageData(sortedResultsHashMap.subMap(newTimestamp, true, + incrementStartTime,false), AnalyzerConstants.MetricName.cpuUsage); PlotData.UsageData memoryUsage = getUsageData(sortedResultsHashMap.subMap(newTimestamp, true, - incrementStartTime, true), AnalyzerConstants.MetricName.memoryUsage); + incrementStartTime, false), AnalyzerConstants.MetricName.memoryUsage); plotsDataMap.put(newTimestamp, new PlotData.PlotPoint(cpuUsage, memoryUsage)); incrementStartTime = newTimestamp; } @@ -85,26 +86,20 @@ PlotData.UsageData getUsageData(Map resultInRange, A .filter(intervalResults -> intervalResults.getMetricResultsMap().containsKey(metricName)) .map(intervalResults -> { MetricResults metricResults = intervalResults.getMetricResultsMap().get(metricName); - double metricUsageAvg = (metricResults != null) ? metricResults.getAggregationInfoResult().getAvg() : 0.0; - double metricUsageMax = (metricResults != null) ? metricResults.getAggregationInfoResult().getMax() : 0.0; - double metricUsageSum = (metricResults != null) ? metricResults.getAggregationInfoResult().getSum() : 0.0; - - double metricUsage = (metricUsageMax > 0) ? metricUsageMax : metricUsageAvg; - - double metricRequestInterval; - double cpuUsagePod = 0; - int numPods; - - if (CPU_ONE_CORE > metricUsage) { - metricRequestInterval = metricUsage; - } else { - if (metricUsageAvg != 0) { - numPods = (int) Math.ceil(metricUsageSum / metricUsageAvg); - cpuUsagePod = (numPods > 0) ? metricUsageSum / numPods : 0.0; - } - metricRequestInterval = Math.max(cpuUsagePod, metricUsage); - } - return metricRequestInterval; + double metricUsageAvg = Optional.ofNullable(metricResults) + .map(MetricResults::getAggregationInfoResult) + .map(MetricAggregationInfoResults::getAvg) + .orElse(0.0); + double metricUsageMax = Optional.ofNullable(metricResults) + .map(MetricResults::getAggregationInfoResult) + .map(MetricAggregationInfoResults::getMax) + .orElse(0.0); + double metricUsageSum = Optional.ofNullable(metricResults) + .map(MetricResults::getAggregationInfoResult) + .map(MetricAggregationInfoResults::getSum) + .orElse(0.0); + + return getMetricRequestInterval(metricUsageMax, metricUsageAvg, metricUsageSum); }) .collect(Collectors.toList()); LOGGER.debug("metricValues : {}", metricValues); @@ -123,4 +118,23 @@ PlotData.UsageData getUsageData(Map resultInRange, A } } + + private static double getMetricRequestInterval(double metricUsageMax, double metricUsageAvg, double metricUsageSum) { + double metricUsage = (metricUsageMax > 0) ? metricUsageMax : metricUsageAvg; + + double metricRequestInterval; + double cpuUsagePod = 0; + int numPods; + + if (CPU_ONE_CORE > metricUsage) { + metricRequestInterval = metricUsage; + } else { + if (metricUsageAvg != 0) { + numPods = (int) Math.ceil(metricUsageSum / metricUsageAvg); + cpuUsagePod = (numPods > 0) ? metricUsageSum / numPods : 0.0; + } + metricRequestInterval = Math.max(cpuUsagePod, metricUsage); + } + return metricRequestInterval; + } } From 28168b516b445de7d7d8435de095680a702f28d4 Mon Sep 17 00:00:00 2001 From: Saad Khan Date: Mon, 6 May 2024 16:34:09 +0530 Subject: [PATCH 05/11] refactor code to extract format and metric values in a single operation Signed-off-by: Saad Khan --- .../autotune/analyzer/plots/PlotManager.java | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/autotune/analyzer/plots/PlotManager.java b/src/main/java/com/autotune/analyzer/plots/PlotManager.java index f7772e73c..035159265 100644 --- a/src/main/java/com/autotune/analyzer/plots/PlotManager.java +++ b/src/main/java/com/autotune/analyzer/plots/PlotManager.java @@ -72,17 +72,8 @@ public PlotData.PlotsData generatePlots() { } PlotData.UsageData getUsageData(Map resultInRange, AnalyzerConstants.MetricName metricName) { - // Extract the format value - String format = resultInRange.values().stream() - .filter(intervalResults -> intervalResults.getMetricResultsMap().containsKey(metricName)) - .map(intervalResults -> intervalResults.getMetricResultsMap().get(metricName)) - .filter(Objects::nonNull) - .map(metricResults -> metricResults.getAggregationInfoResult().getFormat()) - .findFirst() - .orElse(null); - - // Extract metric values - List metricValues = resultInRange.values().stream() + // stream through the results value and extract the format and the metric values + PlotData.UsageData usageData = resultInRange.values().stream() .filter(intervalResults -> intervalResults.getMetricResultsMap().containsKey(metricName)) .map(intervalResults -> { MetricResults metricResults = intervalResults.getMetricResultsMap().get(metricName); @@ -99,24 +90,35 @@ PlotData.UsageData getUsageData(Map resultInRange, A .map(MetricAggregationInfoResults::getSum) .orElse(0.0); - return getMetricRequestInterval(metricUsageMax, metricUsageAvg, metricUsageSum); - }) - .collect(Collectors.toList()); - LOGGER.debug("metricValues : {}", metricValues); - LOGGER.debug("format : {}", format); - if (!metricValues.isEmpty()) { - double q1 = CommonUtils.percentile(TWENTYFIVE_PERCENTILE, metricValues); - double q3 = CommonUtils.percentile(SEVENTYFIVE_PERCENTILE, metricValues); - double median = CommonUtils.percentile(FIFTY_PERCENTILE, metricValues); - // Find max and min - double max = Collections.max(metricValues); - double min = Collections.min(metricValues); - LOGGER.debug("q1 : {}, q3 : {}, median : {}, max : {}, min : {}", q1, q3, median, max, min); - return new PlotData.UsageData(min, q1, median, q3, max, format); - } else { - return null; - } + String format = Optional.ofNullable(metricResults) + .map(MetricResults::getAggregationInfoResult) + .map(MetricAggregationInfoResults::getFormat) + .orElse(null); + return new AbstractMap.SimpleEntry<>(format, getMetricRequestInterval(metricUsageMax, metricUsageAvg, metricUsageSum)); + }) + .filter(entry -> entry.getValue() != null) // Filter out entries where metric is null + .collect(Collectors.collectingAndThen( + Collectors.toList(), + list -> { + if (list.isEmpty()) { + return null; + } else { + List metricValues = list.stream().map(Map.Entry::getValue).collect(Collectors.toList()); + String format = list.get(0).getKey(); // Since format is common, take it from any entry + double q1 = CommonUtils.percentile(TWENTYFIVE_PERCENTILE, metricValues); + double q3 = CommonUtils.percentile(SEVENTYFIVE_PERCENTILE, metricValues); + double median = CommonUtils.percentile(FIFTY_PERCENTILE, metricValues); + // Find max and min + double max = Collections.max(metricValues); + double min = Collections.min(metricValues); + LOGGER.debug("q1 : {}, q3 : {}, median : {}, max : {}, min : {}", q1, q3, median, max, min); + return new PlotData.UsageData(min, q1, median, q3, max, format); + } + })); + + LOGGER.debug("usageData : {}", usageData); + return usageData; } private static double getMetricRequestInterval(double metricUsageMax, double metricUsageAvg, double metricUsageSum) { From 6e9be77d1dce346f923c7d380e04e189835d2288 Mon Sep 17 00:00:00 2001 From: Saad Khan Date: Thu, 9 May 2024 16:17:42 +0530 Subject: [PATCH 06/11] update plot manager code to consider values from the recommendation algo Signed-off-by: Saad Khan --- .../autotune/analyzer/plots/PlotManager.java | 94 +++++----------- .../model/CostBasedRecommendationModel.java | 105 +++++++++--------- .../PerformanceBasedRecommendationModel.java | 37 +----- 3 files changed, 80 insertions(+), 156 deletions(-) diff --git a/src/main/java/com/autotune/analyzer/plots/PlotManager.java b/src/main/java/com/autotune/analyzer/plots/PlotManager.java index 035159265..971eedb85 100644 --- a/src/main/java/com/autotune/analyzer/plots/PlotManager.java +++ b/src/main/java/com/autotune/analyzer/plots/PlotManager.java @@ -1,9 +1,8 @@ package com.autotune.analyzer.plots; +import com.autotune.analyzer.recommendations.model.CostBasedRecommendationModel; import com.autotune.analyzer.recommendations.term.Terms; import com.autotune.analyzer.utils.AnalyzerConstants; -import com.autotune.common.data.metrics.MetricAggregationInfoResults; -import com.autotune.common.data.metrics.MetricResults; import com.autotune.common.data.result.IntervalResults; import com.autotune.common.utils.CommonUtils; import org.slf4j.Logger; @@ -11,10 +10,8 @@ import java.sql.Timestamp; import java.util.*; -import java.util.stream.Collectors; import static com.autotune.analyzer.recommendations.RecommendationConstants.RecommendationEngine.PercentileConstants.*; -import static com.autotune.analyzer.recommendations.RecommendationConstants.RecommendationValueConstants.CPU_ONE_CORE; public class PlotManager { private static final Logger LOGGER = LoggerFactory.getLogger(PlotManager.class); @@ -37,7 +34,7 @@ public PlotData.PlotsData generatePlots() { sortedResultsHashMap.putAll(containerResultsMap); // Retrieve entries within the specified range - Map resultInRange = sortedResultsHashMap.subMap(monitoringEndTime, true, monitoringStartTime, true); + Map resultInRange = sortedResultsHashMap.subMap(monitoringEndTime, true, monitoringStartTime, false); int delimiterNumber = (int) (resultInRange.size() / recommendationTerm.getPlots_datapoints()); @@ -72,71 +69,34 @@ public PlotData.PlotsData generatePlots() { } PlotData.UsageData getUsageData(Map resultInRange, AnalyzerConstants.MetricName metricName) { - // stream through the results value and extract the format and the metric values - PlotData.UsageData usageData = resultInRange.values().stream() - .filter(intervalResults -> intervalResults.getMetricResultsMap().containsKey(metricName)) - .map(intervalResults -> { - MetricResults metricResults = intervalResults.getMetricResultsMap().get(metricName); - double metricUsageAvg = Optional.ofNullable(metricResults) - .map(MetricResults::getAggregationInfoResult) - .map(MetricAggregationInfoResults::getAvg) - .orElse(0.0); - double metricUsageMax = Optional.ofNullable(metricResults) - .map(MetricResults::getAggregationInfoResult) - .map(MetricAggregationInfoResults::getMax) - .orElse(0.0); - double metricUsageSum = Optional.ofNullable(metricResults) - .map(MetricResults::getAggregationInfoResult) - .map(MetricAggregationInfoResults::getSum) - .orElse(0.0); - - String format = Optional.ofNullable(metricResults) - .map(MetricResults::getAggregationInfoResult) - .map(MetricAggregationInfoResults::getFormat) - .orElse(null); - - return new AbstractMap.SimpleEntry<>(format, getMetricRequestInterval(metricUsageMax, metricUsageAvg, metricUsageSum)); - }) - .filter(entry -> entry.getValue() != null) // Filter out entries where metric is null - .collect(Collectors.collectingAndThen( - Collectors.toList(), - list -> { - if (list.isEmpty()) { - return null; - } else { - List metricValues = list.stream().map(Map.Entry::getValue).collect(Collectors.toList()); - String format = list.get(0).getKey(); // Since format is common, take it from any entry - double q1 = CommonUtils.percentile(TWENTYFIVE_PERCENTILE, metricValues); - double q3 = CommonUtils.percentile(SEVENTYFIVE_PERCENTILE, metricValues); - double median = CommonUtils.percentile(FIFTY_PERCENTILE, metricValues); - // Find max and min - double max = Collections.max(metricValues); - double min = Collections.min(metricValues); - LOGGER.debug("q1 : {}, q3 : {}, median : {}, max : {}, min : {}", q1, q3, median, max, min); - return new PlotData.UsageData(min, q1, median, q3, max, format); - } - })); - - LOGGER.debug("usageData : {}", usageData); - return usageData; + // stream through the results value and extract the CPU values + if (metricName.equals(AnalyzerConstants.MetricName.cpuUsage)) { + List cpuValues = CostBasedRecommendationModel.getCPUUsageList(resultInRange); + return getPercentileData(cpuValues, resultInRange, metricName); + } else { + // stream through the results value and extract the memory values + List memUsageList = resultInRange.values() + .stream() + .map(CostBasedRecommendationModel::calculateMemoryUsage) + .toList(); + return getPercentileData(memUsageList, resultInRange, metricName); + } } - private static double getMetricRequestInterval(double metricUsageMax, double metricUsageAvg, double metricUsageSum) { - double metricUsage = (metricUsageMax > 0) ? metricUsageMax : metricUsageAvg; - - double metricRequestInterval; - double cpuUsagePod = 0; - int numPods; - - if (CPU_ONE_CORE > metricUsage) { - metricRequestInterval = metricUsage; + private PlotData.UsageData getPercentileData(List metricValues, Map resultInRange, AnalyzerConstants.MetricName metricName) { + if (!metricValues.isEmpty()) { + LOGGER.debug("metricValues : {}", metricValues); + double q1 = CommonUtils.percentile(TWENTYFIVE_PERCENTILE, metricValues); + double q3 = CommonUtils.percentile(SEVENTYFIVE_PERCENTILE, metricValues); + double median = CommonUtils.percentile(FIFTY_PERCENTILE, metricValues); + // Find max and min + double max = Collections.max(metricValues); + double min = Collections.min(metricValues); + LOGGER.debug("q1 : {}, q3 : {}, median : {}, max : {}, min : {}", q1, q3, median, max, min); + String format = CostBasedRecommendationModel.getFormatValue(resultInRange, metricName); + return new PlotData.UsageData(min, q1, median, q3, max, format); } else { - if (metricUsageAvg != 0) { - numPods = (int) Math.ceil(metricUsageSum / metricUsageAvg); - cpuUsagePod = (numPods > 0) ? metricUsageSum / numPods : 0.0; - } - metricRequestInterval = Math.max(cpuUsagePod, metricUsage); + return null; } - return metricRequestInterval; } } diff --git a/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java b/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java index c9c72a51d..c2c404297 100644 --- a/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java +++ b/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java @@ -42,42 +42,7 @@ public RecommendationConfigItem getCPURequestRecommendation(Map cpuUsageList = filteredResultsMap.values() - .stream() - .map(e -> { - Optional cpuUsageResults = Optional.ofNullable(e.getMetricResultsMap().get(AnalyzerConstants.MetricName.cpuUsage)); - Optional cpuThrottleResults = Optional.ofNullable(e.getMetricResultsMap().get(AnalyzerConstants.MetricName.cpuThrottle)); - double cpuUsageAvg = cpuUsageResults.map(m -> m.getAggregationInfoResult().getAvg()).orElse(0.0); - double cpuUsageMax = cpuUsageResults.map(m -> m.getAggregationInfoResult().getMax()).orElse(0.0); - double cpuUsageSum = cpuUsageResults.map(m -> m.getAggregationInfoResult().getSum()).orElse(0.0); - double cpuThrottleAvg = cpuThrottleResults.map(m -> m.getAggregationInfoResult().getAvg()).orElse(0.0); - double cpuThrottleMax = cpuThrottleResults.map(m -> m.getAggregationInfoResult().getMax()).orElse(0.0); - double cpuThrottleSum = cpuThrottleResults.map(m -> m.getAggregationInfoResult().getSum()).orElse(0.0); - double cpuRequestInterval = 0.0; - double cpuUsagePod = 0; - int numPods = 0; - - // Use the Max value when available, if not use the Avg - double cpuUsage = (cpuUsageMax > 0) ? cpuUsageMax : cpuUsageAvg; - double cpuThrottle = (cpuThrottleMax > 0) ? cpuThrottleMax : cpuThrottleAvg; - double cpuUsageTotal = cpuUsage + cpuThrottle; - - // Usage is less than 1 core, set it to the observed value. - if (CPU_ONE_CORE > cpuUsageTotal) { - cpuRequestInterval = cpuUsageTotal; - } else { - // Sum/Avg should give us the number of pods - if (0 != cpuUsageAvg) { - numPods = (int) Math.ceil(cpuUsageSum / cpuUsageAvg); - if (0 < numPods) { - cpuUsagePod = (cpuUsageSum + cpuThrottleSum) / numPods; - } - } - cpuRequestInterval = Math.max(cpuUsagePod, cpuUsageTotal); - } - return cpuRequestInterval; - }) - .collect(Collectors.toList()); + List cpuUsageList = getCPUUsageList(filteredResultsMap); Double cpuRequest = 0.0; Double cpuRequestMax = Collections.max(cpuUsageList); @@ -116,23 +81,51 @@ else if (CPU_ONE_MILLICORE >= cpuRequest) { } } - for (IntervalResults intervalResults : filteredResultsMap.values()) { - MetricResults cpuUsageResults = intervalResults.getMetricResultsMap().get(AnalyzerConstants.MetricName.cpuUsage); - if (cpuUsageResults != null) { - MetricAggregationInfoResults aggregationInfoResult = cpuUsageResults.getAggregationInfoResult(); - if (aggregationInfoResult != null) { - format = aggregationInfoResult.getFormat(); - if (format != null && !format.isEmpty()) { - break; - } - } - } - } + format = getFormatValue(filteredResultsMap, AnalyzerConstants.MetricName.cpuUsage); recommendationConfigItem = new RecommendationConfigItem(cpuRequest, format); return recommendationConfigItem; } + public static List getCPUUsageList(Map filteredResultsMap) { + return filteredResultsMap.values() + .stream() + .map(e -> { + Optional cpuUsageResults = Optional.ofNullable(e.getMetricResultsMap().get(AnalyzerConstants.MetricName.cpuUsage)); + Optional cpuThrottleResults = Optional.ofNullable(e.getMetricResultsMap().get(AnalyzerConstants.MetricName.cpuThrottle)); + double cpuUsageAvg = cpuUsageResults.map(m -> m.getAggregationInfoResult().getAvg()).orElse(0.0); + double cpuUsageMax = cpuUsageResults.map(m -> m.getAggregationInfoResult().getMax()).orElse(0.0); + double cpuUsageSum = cpuUsageResults.map(m -> m.getAggregationInfoResult().getSum()).orElse(0.0); + double cpuThrottleAvg = cpuThrottleResults.map(m -> m.getAggregationInfoResult().getAvg()).orElse(0.0); + double cpuThrottleMax = cpuThrottleResults.map(m -> m.getAggregationInfoResult().getMax()).orElse(0.0); + double cpuThrottleSum = cpuThrottleResults.map(m -> m.getAggregationInfoResult().getSum()).orElse(0.0); + double cpuRequestInterval = 0.0; + double cpuUsagePod = 0; + int numPods = 0; + + // Use the Max value when available, if not use the Avg + double cpuUsage = (cpuUsageMax > 0) ? cpuUsageMax : cpuUsageAvg; + double cpuThrottle = (cpuThrottleMax > 0) ? cpuThrottleMax : cpuThrottleAvg; + double cpuUsageTotal = cpuUsage + cpuThrottle; + + // Usage is less than 1 core, set it to the observed value. + if (CPU_ONE_CORE > cpuUsageTotal) { + cpuRequestInterval = cpuUsageTotal; + } else { + // Sum/Avg should give us the number of pods + if (0 != cpuUsageAvg) { + numPods = (int) Math.ceil(cpuUsageSum / cpuUsageAvg); + if (0 < numPods) { + cpuUsagePod = (cpuUsageSum + cpuThrottleSum) / numPods; + } + } + cpuRequestInterval = Math.max(cpuUsagePod, cpuUsageTotal); + } + return cpuRequestInterval; + }) + .collect(Collectors.toList()); + } + @Override public RecommendationConfigItem getMemoryRequestRecommendation(Map filteredResultsMap, ArrayList notifications) { @@ -169,8 +162,16 @@ public RecommendationConfigItem getMemoryRequestRecommendation(Map filteredResultsMap, AnalyzerConstants.MetricName metricName) { + String format = ""; for (IntervalResults intervalResults : filteredResultsMap.values()) { - MetricResults memoryUsageResults = intervalResults.getMetricResultsMap().get(AnalyzerConstants.MetricName.memoryUsage); + MetricResults memoryUsageResults = intervalResults.getMetricResultsMap().get(metricName); if (memoryUsageResults != null) { MetricAggregationInfoResults aggregationInfoResult = memoryUsageResults.getAggregationInfoResult(); if (aggregationInfoResult != null) { @@ -181,9 +182,7 @@ public RecommendationConfigItem getMemoryRequestRecommendation(Map cpuUsageResults = Optional.ofNullable(intervalResults.getMetricResultsMap().get(AnalyzerConstants.MetricName.cpuUsage)); double cpuUsageAvg = cpuUsageResults.map(m -> m.getAggregationInfoResult().getAvg()).orElse(0.0); double cpuUsageSum = cpuUsageResults.map(m -> m.getAggregationInfoResult().getSum()).orElse(0.0); diff --git a/src/main/java/com/autotune/analyzer/recommendations/model/PerformanceBasedRecommendationModel.java b/src/main/java/com/autotune/analyzer/recommendations/model/PerformanceBasedRecommendationModel.java index 3409df3b3..7efdb1104 100644 --- a/src/main/java/com/autotune/analyzer/recommendations/model/PerformanceBasedRecommendationModel.java +++ b/src/main/java/com/autotune/analyzer/recommendations/model/PerformanceBasedRecommendationModel.java @@ -45,42 +45,7 @@ public RecommendationConfigItem getCPURequestRecommendation(Map cpuUsageList = filteredResultsMap.values() - .stream() - .map(e -> { - Optional cpuUsageResults = Optional.ofNullable(e.getMetricResultsMap().get(AnalyzerConstants.MetricName.cpuUsage)); - Optional cpuThrottleResults = Optional.ofNullable(e.getMetricResultsMap().get(AnalyzerConstants.MetricName.cpuThrottle)); - double cpuUsageAvg = cpuUsageResults.map(m -> m.getAggregationInfoResult().getAvg()).orElse(0.0); - double cpuUsageMax = cpuUsageResults.map(m -> m.getAggregationInfoResult().getMax()).orElse(0.0); - double cpuUsageSum = cpuUsageResults.map(m -> m.getAggregationInfoResult().getSum()).orElse(0.0); - double cpuThrottleAvg = cpuThrottleResults.map(m -> m.getAggregationInfoResult().getAvg()).orElse(0.0); - double cpuThrottleMax = cpuThrottleResults.map(m -> m.getAggregationInfoResult().getMax()).orElse(0.0); - double cpuThrottleSum = cpuThrottleResults.map(m -> m.getAggregationInfoResult().getSum()).orElse(0.0); - double cpuRequestInterval = 0.0; - double cpuUsagePod = 0; - int numPods = 0; - - // Use the Max value when available, if not use the Avg - double cpuUsage = (cpuUsageMax > 0) ? cpuUsageMax : cpuUsageAvg; - double cpuThrottle = (cpuThrottleMax > 0) ? cpuThrottleMax : cpuThrottleAvg; - double cpuUsageTotal = cpuUsage + cpuThrottle; - - // Usage is less than 1 core, set it to the observed value. - if (CPU_ONE_CORE > cpuUsageTotal) { - cpuRequestInterval = cpuUsageTotal; - } else { - // Sum/Avg should give us the number of pods - if (0 != cpuUsageAvg) { - numPods = (int) Math.ceil(cpuUsageSum / cpuUsageAvg); - if (0 < numPods) { - cpuUsagePod = (cpuUsageSum + cpuThrottleSum) / numPods; - } - } - cpuRequestInterval = Math.max(cpuUsagePod, cpuUsageTotal); - } - return cpuRequestInterval; - }) - .collect(Collectors.toList()); + List cpuUsageList = CostBasedRecommendationModel.getCPUUsageList(filteredResultsMap); Double cpuRequest = 0.0; Double cpuRequestMax = Collections.max(cpuUsageList); From 463b92e1b4403d51df26dfafb46b5cc2bf0c3c7f Mon Sep 17 00:00:00 2001 From: Saad Khan Date: Fri, 10 May 2024 22:09:06 +0530 Subject: [PATCH 07/11] add min value for the percentiles, fix exceptions due to immutable list and other minor fixes Signed-off-by: Saad Khan --- .../autotune/analyzer/plots/PlotManager.java | 89 +++++++++---- .../model/CostBasedRecommendationModel.java | 126 +++++++++++------- .../PerformanceBasedRecommendationModel.java | 20 ++- 3 files changed, 160 insertions(+), 75 deletions(-) diff --git a/src/main/java/com/autotune/analyzer/plots/PlotManager.java b/src/main/java/com/autotune/analyzer/plots/PlotManager.java index 971eedb85..a5ec40410 100644 --- a/src/main/java/com/autotune/analyzer/plots/PlotManager.java +++ b/src/main/java/com/autotune/analyzer/plots/PlotManager.java @@ -5,6 +5,10 @@ import com.autotune.analyzer.utils.AnalyzerConstants; import com.autotune.common.data.result.IntervalResults; import com.autotune.common.utils.CommonUtils; +import com.autotune.utils.KruizeConstants; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -70,33 +74,70 @@ public PlotData.PlotsData generatePlots() { PlotData.UsageData getUsageData(Map resultInRange, AnalyzerConstants.MetricName metricName) { // stream through the results value and extract the CPU values - if (metricName.equals(AnalyzerConstants.MetricName.cpuUsage)) { - List cpuValues = CostBasedRecommendationModel.getCPUUsageList(resultInRange); - return getPercentileData(cpuValues, resultInRange, metricName); - } else { - // stream through the results value and extract the memory values - List memUsageList = resultInRange.values() - .stream() - .map(CostBasedRecommendationModel::calculateMemoryUsage) - .toList(); - return getPercentileData(memUsageList, resultInRange, metricName); + try { + if (metricName.equals(AnalyzerConstants.MetricName.cpuUsage)) { + JSONArray cpuValues = CostBasedRecommendationModel.getCPUUsageList(resultInRange); + LOGGER.debug("cpuValues : {}", cpuValues); + if (!cpuValues.isEmpty()) { + // Extract "max" values from cpuUsageList + List cpuMaxValues = new ArrayList<>(); + List cpuMinValues = new ArrayList<>(); + for (int i = 0; i < cpuValues.length(); i++) { + JSONObject jsonObject = cpuValues.getJSONObject(i); + double maxValue = jsonObject.getDouble(KruizeConstants.JSONKeys.MAX); + double minValue = jsonObject.getDouble(KruizeConstants.JSONKeys.MIN); + cpuMaxValues.add(maxValue); + cpuMinValues.add(minValue); + } + LOGGER.debug("cpuMaxValues : {}", cpuMaxValues); + LOGGER.debug("cpuMinValues : {}", cpuMinValues); + return getPercentileData(cpuMaxValues, cpuMinValues, resultInRange, metricName); + } + + } else { + // loop through the results value and extract the memory values + CostBasedRecommendationModel costBasedRecommendationModel = new CostBasedRecommendationModel(); + List memUsageMinList = new ArrayList<>(); + List memUsageMaxList = new ArrayList<>(); + boolean memDataAvailable = false; + for (IntervalResults intervalResults: resultInRange.values()) { + JSONObject jsonObject = costBasedRecommendationModel.calculateMemoryUsage(intervalResults); + if (!jsonObject.isEmpty()) { + memDataAvailable = true; + Double memUsageMax = jsonObject.getDouble(KruizeConstants.JSONKeys.MAX); + Double memUsageMin = jsonObject.getDouble(KruizeConstants.JSONKeys.MIN); + memUsageMaxList.add(memUsageMax); + memUsageMinList.add(memUsageMin); + } + } + LOGGER.debug("memValues Max : {}, Min : {}", memUsageMaxList, memUsageMinList); + if (memDataAvailable) + return getPercentileData(memUsageMaxList, memUsageMinList, resultInRange, metricName); + } + } catch (JSONException e) { + LOGGER.error("Exception occurred while extracting metric values: {}", e.getMessage()); } + return null; } - private PlotData.UsageData getPercentileData(List metricValues, Map resultInRange, AnalyzerConstants.MetricName metricName) { - if (!metricValues.isEmpty()) { - LOGGER.debug("metricValues : {}", metricValues); - double q1 = CommonUtils.percentile(TWENTYFIVE_PERCENTILE, metricValues); - double q3 = CommonUtils.percentile(SEVENTYFIVE_PERCENTILE, metricValues); - double median = CommonUtils.percentile(FIFTY_PERCENTILE, metricValues); - // Find max and min - double max = Collections.max(metricValues); - double min = Collections.min(metricValues); - LOGGER.debug("q1 : {}, q3 : {}, median : {}, max : {}, min : {}", q1, q3, median, max, min); - String format = CostBasedRecommendationModel.getFormatValue(resultInRange, metricName); - return new PlotData.UsageData(min, q1, median, q3, max, format); - } else { - return null; + private PlotData.UsageData getPercentileData(List metricValuesMax, List metricValuesMin, Map resultInRange, AnalyzerConstants.MetricName metricName) { + try { + if (!metricValuesMax.isEmpty()) { + double q1 = CommonUtils.percentile(TWENTYFIVE_PERCENTILE, metricValuesMax); + double q3 = CommonUtils.percentile(SEVENTYFIVE_PERCENTILE, metricValuesMax); + double median = CommonUtils.percentile(FIFTY_PERCENTILE, metricValuesMax); + // Find max and min + double max = Collections.max(metricValuesMax); + double min = Collections.min(metricValuesMin); + LOGGER.debug("q1 : {}, q3 : {}, median : {}, max : {}, min : {}", q1, q3, median, max, min); + String format = CostBasedRecommendationModel.getFormatValue(resultInRange, metricName); + return new PlotData.UsageData(min, q1, median, q3, max, format); + } else { + return null; + } + } catch (Exception e) { + LOGGER.error("Exception occurred while generating percentiles: {}", e.getMessage()); } + return null; } } diff --git a/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java b/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java index c2c404297..90d8b207f 100644 --- a/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java +++ b/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java @@ -8,12 +8,16 @@ import com.autotune.common.data.metrics.MetricResults; import com.autotune.common.data.result.IntervalResults; import com.autotune.common.utils.CommonUtils; +import com.autotune.utils.KruizeConstants; +import org.json.JSONArray; +import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.Timestamp; import java.util.*; import java.util.stream.Collectors; +import java.util.stream.IntStream; import static com.autotune.analyzer.recommendations.RecommendationConstants.RecommendationEngine.PercentileConstants.COST_CPU_PERCENTILE; import static com.autotune.analyzer.recommendations.RecommendationConstants.RecommendationEngine.PercentileConstants.COST_MEMORY_PERCENTILE; @@ -42,14 +46,19 @@ public RecommendationConfigItem getCPURequestRecommendation(Map cpuUsageList = getCPUUsageList(filteredResultsMap); + JSONArray cpuUsageList = getCPUUsageList(filteredResultsMap); + // Extract 'max' values from cpuUsageList + List cpuMaxValues = IntStream.range(0, cpuUsageList.length()) + .mapToObj(cpuUsageList::getJSONObject) + .map(jsonObject -> jsonObject.getDouble(KruizeConstants.JSONKeys.MAX)) + .toList(); - Double cpuRequest = 0.0; - Double cpuRequestMax = Collections.max(cpuUsageList); + Double cpuRequest; + Double cpuRequestMax = Collections.max(cpuMaxValues); if (null != cpuRequestMax && CPU_ONE_CORE > cpuRequestMax) { cpuRequest = cpuRequestMax; } else { - cpuRequest = CommonUtils.percentile(COST_CPU_PERCENTILE, cpuUsageList); + cpuRequest = CommonUtils.percentile(COST_CPU_PERCENTILE, cpuMaxValues); } // TODO: This code below should be optimised with idle detection (0 cpu usage in recorded data) in recommendation ALGO @@ -87,45 +96,54 @@ else if (CPU_ONE_MILLICORE >= cpuRequest) { return recommendationConfigItem; } - public static List getCPUUsageList(Map filteredResultsMap) { - return filteredResultsMap.values() - .stream() - .map(e -> { - Optional cpuUsageResults = Optional.ofNullable(e.getMetricResultsMap().get(AnalyzerConstants.MetricName.cpuUsage)); - Optional cpuThrottleResults = Optional.ofNullable(e.getMetricResultsMap().get(AnalyzerConstants.MetricName.cpuThrottle)); - double cpuUsageAvg = cpuUsageResults.map(m -> m.getAggregationInfoResult().getAvg()).orElse(0.0); - double cpuUsageMax = cpuUsageResults.map(m -> m.getAggregationInfoResult().getMax()).orElse(0.0); - double cpuUsageSum = cpuUsageResults.map(m -> m.getAggregationInfoResult().getSum()).orElse(0.0); - double cpuThrottleAvg = cpuThrottleResults.map(m -> m.getAggregationInfoResult().getAvg()).orElse(0.0); - double cpuThrottleMax = cpuThrottleResults.map(m -> m.getAggregationInfoResult().getMax()).orElse(0.0); - double cpuThrottleSum = cpuThrottleResults.map(m -> m.getAggregationInfoResult().getSum()).orElse(0.0); - double cpuRequestInterval = 0.0; - double cpuUsagePod = 0; - int numPods = 0; - - // Use the Max value when available, if not use the Avg - double cpuUsage = (cpuUsageMax > 0) ? cpuUsageMax : cpuUsageAvg; - double cpuThrottle = (cpuThrottleMax > 0) ? cpuThrottleMax : cpuThrottleAvg; - double cpuUsageTotal = cpuUsage + cpuThrottle; - - // Usage is less than 1 core, set it to the observed value. - if (CPU_ONE_CORE > cpuUsageTotal) { - cpuRequestInterval = cpuUsageTotal; - } else { - // Sum/Avg should give us the number of pods - if (0 != cpuUsageAvg) { - numPods = (int) Math.ceil(cpuUsageSum / cpuUsageAvg); - if (0 < numPods) { - cpuUsagePod = (cpuUsageSum + cpuThrottleSum) / numPods; - } - } - cpuRequestInterval = Math.max(cpuUsagePod, cpuUsageTotal); + public static JSONArray getCPUUsageList(Map filteredResultsMap) { + JSONArray cpuRequestIntervalArray = new JSONArray(); + for (IntervalResults intervalResults : filteredResultsMap.values()) { + JSONObject cpuRequestInterval = new JSONObject(); + Optional cpuUsageResults = Optional.ofNullable(intervalResults.getMetricResultsMap().get(AnalyzerConstants.MetricName.cpuUsage)); + Optional cpuThrottleResults = Optional.ofNullable(intervalResults.getMetricResultsMap().get(AnalyzerConstants.MetricName.cpuThrottle)); + double cpuUsageAvg = cpuUsageResults.map(m -> m.getAggregationInfoResult().getAvg()).orElse(0.0); + double cpuUsageMax = cpuUsageResults.map(m -> m.getAggregationInfoResult().getMax()).orElse(0.0); + double cpuUsageSum = cpuUsageResults.map(m -> m.getAggregationInfoResult().getSum()).orElse(0.0); + double cpuUsageMin = cpuUsageResults.map(m -> m.getAggregationInfoResult().getMin()).orElse(0.0); + double cpuThrottleAvg = cpuThrottleResults.map(m -> m.getAggregationInfoResult().getAvg()).orElse(0.0); + double cpuThrottleMax = cpuThrottleResults.map(m -> m.getAggregationInfoResult().getMax()).orElse(0.0); + double cpuThrottleSum = cpuThrottleResults.map(m -> m.getAggregationInfoResult().getSum()).orElse(0.0); + double cpuThrottleMin = cpuThrottleResults.map(m -> m.getAggregationInfoResult().getMin()).orElse(0.0); + + double cpuRequestIntervalMax; + double cpuRequestIntervalMin; + double cpuUsagePod = 0; + int numPods; + + // Use the Max value when available, if not use the Avg + double cpuUsage = (cpuUsageMax > 0) ? cpuUsageMax : cpuUsageAvg; + double cpuThrottle = (cpuThrottleMax > 0) ? cpuThrottleMax : cpuThrottleAvg; + double cpuUsageTotal = cpuUsage + cpuThrottle; + + // Usage is less than 1 core, set it to the observed value. + if (CPU_ONE_CORE > cpuUsageTotal) { + cpuRequestIntervalMax = cpuUsageTotal; + } else { + // Sum/Avg should give us the number of pods + if (0 != cpuUsageAvg) { + numPods = (int) Math.ceil(cpuUsageSum / cpuUsageAvg); + if (0 < numPods) { + cpuUsagePod = (cpuUsageSum + cpuThrottleSum) / numPods; } - return cpuRequestInterval; - }) - .collect(Collectors.toList()); + } + cpuRequestIntervalMax = Math.max(cpuUsagePod, cpuUsageTotal); + } + double cpuMinTotal = cpuUsageMin + cpuThrottleMin; + cpuRequestIntervalMin = Collections.min(Arrays.asList(cpuUsagePod, cpuUsageTotal, cpuMinTotal)); + cpuRequestInterval.put(KruizeConstants.JSONKeys.MAX, cpuRequestIntervalMax); + if (cpuRequestIntervalMin > 0.0) + cpuRequestInterval.put(KruizeConstants.JSONKeys.MIN, cpuRequestIntervalMin); + LOGGER.debug("cpuRequestInterval : {}", cpuRequestInterval); + cpuRequestIntervalArray.put(cpuRequestInterval); + } + return cpuRequestIntervalArray; } - @Override public RecommendationConfigItem getMemoryRequestRecommendation(Map filteredResultsMap, ArrayList notifications) { @@ -136,10 +154,13 @@ public RecommendationConfigItem getMemoryRequestRecommendation(Map memUsageList = filteredResultsMap.values() - .stream() - .map(CostBasedRecommendationModel::calculateMemoryUsage) - .collect(Collectors.toList()); + CostBasedRecommendationModel costBasedRecommendationModel = new CostBasedRecommendationModel(); + List memUsageList = new ArrayList<>(); + for (IntervalResults intervalResults: filteredResultsMap.values()) { + JSONObject jsonObject = costBasedRecommendationModel.calculateMemoryUsage(intervalResults); + Double memUsage = jsonObject.getDouble(KruizeConstants.JSONKeys.MAX); + memUsageList.add(memUsage); + } List spikeList = filteredResultsMap.values() .stream() @@ -195,13 +216,16 @@ public void validate() { } - public static double calculateMemoryUsage(IntervalResults intervalResults) { + public static JSONObject calculateMemoryUsage(IntervalResults intervalResults) { + // create a JSON object which should be returned here having two values, Math.max and Collections.Min + JSONObject jsonObject = new JSONObject(); Optional cpuUsageResults = Optional.ofNullable(intervalResults.getMetricResultsMap().get(AnalyzerConstants.MetricName.cpuUsage)); double cpuUsageAvg = cpuUsageResults.map(m -> m.getAggregationInfoResult().getAvg()).orElse(0.0); double cpuUsageSum = cpuUsageResults.map(m -> m.getAggregationInfoResult().getSum()).orElse(0.0); Optional memoryUsageResults = Optional.ofNullable(intervalResults.getMetricResultsMap().get(AnalyzerConstants.MetricName.memoryUsage)); double memUsageAvg = memoryUsageResults.map(m -> m.getAggregationInfoResult().getAvg()).orElse(0.0); double memUsageMax = memoryUsageResults.map(m -> m.getAggregationInfoResult().getMax()).orElse(0.0); + double memUsageMin = memoryUsageResults.map(m -> m.getAggregationInfoResult().getMin()).orElse(0.0); double memUsageSum = memoryUsageResults.map(m -> m.getAggregationInfoResult().getSum()).orElse(0.0); double memUsage = 0; int numPods = 0; @@ -215,9 +239,15 @@ public static double calculateMemoryUsage(IntervalResults intervalResults) { if (0 < numPods) { memUsage = (memUsageSum / numPods); } - memUsage = Math.max(memUsage, memUsageMax); + memUsageMax = Math.max(memUsage, memUsageMax); + memUsageMin = Collections.min(Arrays.asList(memUsage, memUsageMax, memUsageMin)); + + jsonObject.put(KruizeConstants.JSONKeys.MAX, memUsageMax); + if (memUsageMin > 0.0) + jsonObject.put(KruizeConstants.JSONKeys.MIN, memUsageMin); - return memUsage; + LOGGER.debug("memRequestInterval : {}", jsonObject); + return jsonObject; } private static double calculateIntervalSpike(IntervalResults intervalResults) { diff --git a/src/main/java/com/autotune/analyzer/recommendations/model/PerformanceBasedRecommendationModel.java b/src/main/java/com/autotune/analyzer/recommendations/model/PerformanceBasedRecommendationModel.java index 7efdb1104..617976e98 100644 --- a/src/main/java/com/autotune/analyzer/recommendations/model/PerformanceBasedRecommendationModel.java +++ b/src/main/java/com/autotune/analyzer/recommendations/model/PerformanceBasedRecommendationModel.java @@ -9,12 +9,15 @@ import com.autotune.common.data.metrics.MetricResults; import com.autotune.common.data.result.IntervalResults; import com.autotune.common.utils.CommonUtils; +import com.autotune.utils.KruizeConstants; +import org.json.JSONArray; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.Timestamp; import java.util.*; import java.util.stream.Collectors; +import java.util.stream.IntStream; import static com.autotune.analyzer.recommendations.RecommendationConstants.RecommendationEngine.PercentileConstants.PERFORMANCE_CPU_PERCENTILE; import static com.autotune.analyzer.recommendations.RecommendationConstants.RecommendationEngine.PercentileConstants.PERFORMANCE_MEMORY_PERCENTILE; @@ -45,14 +48,25 @@ public RecommendationConfigItem getCPURequestRecommendation(Map cpuUsageList = CostBasedRecommendationModel.getCPUUsageList(filteredResultsMap); + JSONArray cpuUsageList = CostBasedRecommendationModel.getCPUUsageList(filteredResultsMap); + LOGGER.info("cpuUsageList : {}", cpuUsageList); + // Extract "max" values from cpuUsageList + List cpuMaxValues = new ArrayList<>(); + try { + cpuMaxValues = IntStream.range(0, cpuUsageList.length()) + .mapToObj(cpuUsageList::getJSONObject) + .map(jsonObject -> jsonObject.getDouble(KruizeConstants.JSONKeys.MAX)) + .toList(); + } catch (Exception e) { + e.printStackTrace(); + } Double cpuRequest = 0.0; - Double cpuRequestMax = Collections.max(cpuUsageList); + Double cpuRequestMax = Collections.max(cpuMaxValues); if (null != cpuRequestMax && CPU_ONE_CORE > cpuRequestMax) { cpuRequest = cpuRequestMax; } else { - cpuRequest = CommonUtils.percentile(PERFORMANCE_CPU_PERCENTILE, cpuUsageList); + cpuRequest = CommonUtils.percentile(PERFORMANCE_CPU_PERCENTILE, cpuMaxValues); } // TODO: This code below should be optimised with idle detection (0 cpu usage in recorded data) in recommendation ALGO From 407f94d77a48f9b4403a292d302989fa0a35fc88 Mon Sep 17 00:00:00 2001 From: Saad Khan Date: Fri, 10 May 2024 22:26:57 +0530 Subject: [PATCH 08/11] fix issue in case of zero min values Signed-off-by: Saad Khan --- src/main/java/com/autotune/analyzer/plots/PlotManager.java | 4 ++-- .../model/PerformanceBasedRecommendationModel.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/autotune/analyzer/plots/PlotManager.java b/src/main/java/com/autotune/analyzer/plots/PlotManager.java index a5ec40410..7d246a700 100644 --- a/src/main/java/com/autotune/analyzer/plots/PlotManager.java +++ b/src/main/java/com/autotune/analyzer/plots/PlotManager.java @@ -85,7 +85,7 @@ PlotData.UsageData getUsageData(Map resultInRange, A for (int i = 0; i < cpuValues.length(); i++) { JSONObject jsonObject = cpuValues.getJSONObject(i); double maxValue = jsonObject.getDouble(KruizeConstants.JSONKeys.MAX); - double minValue = jsonObject.getDouble(KruizeConstants.JSONKeys.MIN); + double minValue = jsonObject.optDouble(KruizeConstants.JSONKeys.MIN, 0.0); cpuMaxValues.add(maxValue); cpuMinValues.add(minValue); } @@ -105,7 +105,7 @@ PlotData.UsageData getUsageData(Map resultInRange, A if (!jsonObject.isEmpty()) { memDataAvailable = true; Double memUsageMax = jsonObject.getDouble(KruizeConstants.JSONKeys.MAX); - Double memUsageMin = jsonObject.getDouble(KruizeConstants.JSONKeys.MIN); + Double memUsageMin = jsonObject.optDouble(KruizeConstants.JSONKeys.MIN, 0.0); memUsageMaxList.add(memUsageMax); memUsageMinList.add(memUsageMin); } diff --git a/src/main/java/com/autotune/analyzer/recommendations/model/PerformanceBasedRecommendationModel.java b/src/main/java/com/autotune/analyzer/recommendations/model/PerformanceBasedRecommendationModel.java index 617976e98..191ab102c 100644 --- a/src/main/java/com/autotune/analyzer/recommendations/model/PerformanceBasedRecommendationModel.java +++ b/src/main/java/com/autotune/analyzer/recommendations/model/PerformanceBasedRecommendationModel.java @@ -49,7 +49,7 @@ public RecommendationConfigItem getCPURequestRecommendation(Map cpuMaxValues = new ArrayList<>(); try { From f203777372945fea49f3c70c87c8d8cee029d808 Mon Sep 17 00:00:00 2001 From: Saad Khan Date: Sat, 11 May 2024 00:25:52 +0530 Subject: [PATCH 09/11] ignore min in case of all zero values Signed-off-by: Saad Khan --- .../java/com/autotune/analyzer/plots/PlotData.java | 6 +++--- .../java/com/autotune/analyzer/plots/PlotManager.java | 10 +++++++--- .../model/CostBasedRecommendationModel.java | 11 +++++++---- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/autotune/analyzer/plots/PlotData.java b/src/main/java/com/autotune/analyzer/plots/PlotData.java index ffd8cf97b..28a42c602 100644 --- a/src/main/java/com/autotune/analyzer/plots/PlotData.java +++ b/src/main/java/com/autotune/analyzer/plots/PlotData.java @@ -8,15 +8,15 @@ public class PlotData { public static class UsageData { - public double min; + public Double min; public double q1; public double median; public double q3; public double max; public String format; - public UsageData(double min, double q1, double median, double q3, double max, String format) { - this.min = min; + public UsageData(Double min, double q1, double median, double q3, double max, String format) { + this.min = (min != null && min.equals(0.0)) ? null : min; // Conditionally set min to null if it's 0.0 this.q1 = q1; this.median = median; this.q3 = q3; diff --git a/src/main/java/com/autotune/analyzer/plots/PlotManager.java b/src/main/java/com/autotune/analyzer/plots/PlotManager.java index 7d246a700..d3631a5ee 100644 --- a/src/main/java/com/autotune/analyzer/plots/PlotManager.java +++ b/src/main/java/com/autotune/analyzer/plots/PlotManager.java @@ -85,7 +85,7 @@ PlotData.UsageData getUsageData(Map resultInRange, A for (int i = 0; i < cpuValues.length(); i++) { JSONObject jsonObject = cpuValues.getJSONObject(i); double maxValue = jsonObject.getDouble(KruizeConstants.JSONKeys.MAX); - double minValue = jsonObject.optDouble(KruizeConstants.JSONKeys.MIN, 0.0); + double minValue = jsonObject.getDouble(KruizeConstants.JSONKeys.MIN); cpuMaxValues.add(maxValue); cpuMinValues.add(minValue); } @@ -105,7 +105,7 @@ PlotData.UsageData getUsageData(Map resultInRange, A if (!jsonObject.isEmpty()) { memDataAvailable = true; Double memUsageMax = jsonObject.getDouble(KruizeConstants.JSONKeys.MAX); - Double memUsageMin = jsonObject.optDouble(KruizeConstants.JSONKeys.MIN, 0.0); + Double memUsageMin = jsonObject.getDouble(KruizeConstants.JSONKeys.MIN); memUsageMaxList.add(memUsageMax); memUsageMinList.add(memUsageMin); } @@ -128,7 +128,11 @@ private PlotData.UsageData getPercentileData(List metricValuesMax, List< double median = CommonUtils.percentile(FIFTY_PERCENTILE, metricValuesMax); // Find max and min double max = Collections.max(metricValuesMax); - double min = Collections.min(metricValuesMin); + Double min = null; + boolean zeroesCheck = metricValuesMin.stream().allMatch(value -> value.equals(0.0)); + if (!zeroesCheck) + min = Collections.min(metricValuesMin); + LOGGER.debug("q1 : {}, q3 : {}, median : {}, max : {}, min : {}", q1, q3, median, max, min); String format = CostBasedRecommendationModel.getFormatValue(resultInRange, metricName); return new PlotData.UsageData(min, q1, median, q3, max, format); diff --git a/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java b/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java index 90d8b207f..81538bdb7 100644 --- a/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java +++ b/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java @@ -136,9 +136,11 @@ public static JSONArray getCPUUsageList(Map filtered } double cpuMinTotal = cpuUsageMin + cpuThrottleMin; cpuRequestIntervalMin = Collections.min(Arrays.asList(cpuUsagePod, cpuUsageTotal, cpuMinTotal)); + if (cpuRequestIntervalMin == 0.0) + cpuRequestIntervalMin = Collections.min(Arrays.asList(cpuUsagePod, cpuUsageTotal)); + + cpuRequestInterval.put(KruizeConstants.JSONKeys.MIN, cpuRequestIntervalMin); cpuRequestInterval.put(KruizeConstants.JSONKeys.MAX, cpuRequestIntervalMax); - if (cpuRequestIntervalMin > 0.0) - cpuRequestInterval.put(KruizeConstants.JSONKeys.MIN, cpuRequestIntervalMin); LOGGER.debug("cpuRequestInterval : {}", cpuRequestInterval); cpuRequestIntervalArray.put(cpuRequestInterval); } @@ -241,10 +243,11 @@ public static JSONObject calculateMemoryUsage(IntervalResults intervalResults) { } memUsageMax = Math.max(memUsage, memUsageMax); memUsageMin = Collections.min(Arrays.asList(memUsage, memUsageMax, memUsageMin)); + if (memUsageMin == 0.0) + memUsageMin = Collections.min(Arrays.asList(memUsage, memUsageMax)); + jsonObject.put(KruizeConstants.JSONKeys.MIN, memUsageMin); jsonObject.put(KruizeConstants.JSONKeys.MAX, memUsageMax); - if (memUsageMin > 0.0) - jsonObject.put(KruizeConstants.JSONKeys.MIN, memUsageMin); LOGGER.debug("memRequestInterval : {}", jsonObject); return jsonObject; From 94e42add7d1bfe04394ae7035bff62e70bdb3961 Mon Sep 17 00:00:00 2001 From: Saad Khan Date: Mon, 13 May 2024 12:36:23 +0530 Subject: [PATCH 10/11] refactor logic to get the min value Signed-off-by: Saad Khan --- .../com/autotune/analyzer/plots/PlotData.java | 6 +++--- .../autotune/analyzer/plots/PlotManager.java | 10 +++++++--- .../model/CostBasedRecommendationModel.java | 17 +++++++++++------ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/autotune/analyzer/plots/PlotData.java b/src/main/java/com/autotune/analyzer/plots/PlotData.java index 28a42c602..ffd8cf97b 100644 --- a/src/main/java/com/autotune/analyzer/plots/PlotData.java +++ b/src/main/java/com/autotune/analyzer/plots/PlotData.java @@ -8,15 +8,15 @@ public class PlotData { public static class UsageData { - public Double min; + public double min; public double q1; public double median; public double q3; public double max; public String format; - public UsageData(Double min, double q1, double median, double q3, double max, String format) { - this.min = (min != null && min.equals(0.0)) ? null : min; // Conditionally set min to null if it's 0.0 + public UsageData(double min, double q1, double median, double q3, double max, String format) { + this.min = min; this.q1 = q1; this.median = median; this.q3 = q3; diff --git a/src/main/java/com/autotune/analyzer/plots/PlotManager.java b/src/main/java/com/autotune/analyzer/plots/PlotManager.java index d3631a5ee..16a5efc2c 100644 --- a/src/main/java/com/autotune/analyzer/plots/PlotManager.java +++ b/src/main/java/com/autotune/analyzer/plots/PlotManager.java @@ -128,10 +128,14 @@ private PlotData.UsageData getPercentileData(List metricValuesMax, List< double median = CommonUtils.percentile(FIFTY_PERCENTILE, metricValuesMax); // Find max and min double max = Collections.max(metricValuesMax); - Double min = null; - boolean zeroesCheck = metricValuesMin.stream().allMatch(value -> value.equals(0.0)); - if (!zeroesCheck) + double min; + // check for non zero values + boolean nonZeroCheck = metricValuesMin.stream().noneMatch(value -> value.equals(0.0)); + if (nonZeroCheck) { min = Collections.min(metricValuesMin); + } else { + min = 0.0; + } LOGGER.debug("q1 : {}, q3 : {}, median : {}, max : {}, min : {}", q1, q3, median, max, min); String format = CostBasedRecommendationModel.getFormatValue(resultInRange, metricName); diff --git a/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java b/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java index 81538bdb7..18383e29a 100644 --- a/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java +++ b/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java @@ -18,6 +18,7 @@ import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; +import java.util.stream.Stream; import static com.autotune.analyzer.recommendations.RecommendationConstants.RecommendationEngine.PercentileConstants.COST_CPU_PERCENTILE; import static com.autotune.analyzer.recommendations.RecommendationConstants.RecommendationEngine.PercentileConstants.COST_MEMORY_PERCENTILE; @@ -135,9 +136,11 @@ public static JSONArray getCPUUsageList(Map filtered cpuRequestIntervalMax = Math.max(cpuUsagePod, cpuUsageTotal); } double cpuMinTotal = cpuUsageMin + cpuThrottleMin; - cpuRequestIntervalMin = Collections.min(Arrays.asList(cpuUsagePod, cpuUsageTotal, cpuMinTotal)); - if (cpuRequestIntervalMin == 0.0) - cpuRequestIntervalMin = Collections.min(Arrays.asList(cpuUsagePod, cpuUsageTotal)); + // traverse over a stream of positive values and find the minimum value + cpuRequestIntervalMin = Stream.of(cpuUsagePod, cpuUsageTotal, cpuMinTotal) + .filter(value -> value > 0.0) + .min(Double::compare) + .orElse(0.0); cpuRequestInterval.put(KruizeConstants.JSONKeys.MIN, cpuRequestIntervalMin); cpuRequestInterval.put(KruizeConstants.JSONKeys.MAX, cpuRequestIntervalMax); @@ -242,9 +245,11 @@ public static JSONObject calculateMemoryUsage(IntervalResults intervalResults) { memUsage = (memUsageSum / numPods); } memUsageMax = Math.max(memUsage, memUsageMax); - memUsageMin = Collections.min(Arrays.asList(memUsage, memUsageMax, memUsageMin)); - if (memUsageMin == 0.0) - memUsageMin = Collections.min(Arrays.asList(memUsage, memUsageMax)); + // traverse over a stream of positive values and find the minimum value + memUsageMin = Stream.of(memUsage, memUsageMax, memUsageMin) + .filter(value -> value > 0.0) + .min(Double::compare) + .orElse(0.0); jsonObject.put(KruizeConstants.JSONKeys.MIN, memUsageMin); jsonObject.put(KruizeConstants.JSONKeys.MAX, memUsageMax); From 4960ab1f2c2f78ae590e7dd81c885ee0ecfa3595 Mon Sep 17 00:00:00 2001 From: Saad Khan Date: Mon, 13 May 2024 16:38:35 +0530 Subject: [PATCH 11/11] fix issue with stream operation causing test failures Signed-off-by: Saad Khan --- .../model/CostBasedRecommendationModel.java | 10 ++++++---- .../model/PerformanceBasedRecommendationModel.java | 12 +++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java b/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java index 18383e29a..ae506b2e0 100644 --- a/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java +++ b/src/main/java/com/autotune/analyzer/recommendations/model/CostBasedRecommendationModel.java @@ -49,10 +49,12 @@ public RecommendationConfigItem getCPURequestRecommendation(Map cpuMaxValues = IntStream.range(0, cpuUsageList.length()) - .mapToObj(cpuUsageList::getJSONObject) - .map(jsonObject -> jsonObject.getDouble(KruizeConstants.JSONKeys.MAX)) - .toList(); + List cpuMaxValues = new ArrayList<>(); + for (int i = 0; i < cpuUsageList.length(); i++) { + JSONObject jsonObject = cpuUsageList.getJSONObject(i); + double maxValue = jsonObject.getDouble(KruizeConstants.JSONKeys.MAX); + cpuMaxValues.add(maxValue); + } Double cpuRequest; Double cpuRequestMax = Collections.max(cpuMaxValues); diff --git a/src/main/java/com/autotune/analyzer/recommendations/model/PerformanceBasedRecommendationModel.java b/src/main/java/com/autotune/analyzer/recommendations/model/PerformanceBasedRecommendationModel.java index 191ab102c..27febf51a 100644 --- a/src/main/java/com/autotune/analyzer/recommendations/model/PerformanceBasedRecommendationModel.java +++ b/src/main/java/com/autotune/analyzer/recommendations/model/PerformanceBasedRecommendationModel.java @@ -11,6 +11,7 @@ import com.autotune.common.utils.CommonUtils; import com.autotune.utils.KruizeConstants; import org.json.JSONArray; +import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,13 +53,10 @@ public RecommendationConfigItem getCPURequestRecommendation(Map cpuMaxValues = new ArrayList<>(); - try { - cpuMaxValues = IntStream.range(0, cpuUsageList.length()) - .mapToObj(cpuUsageList::getJSONObject) - .map(jsonObject -> jsonObject.getDouble(KruizeConstants.JSONKeys.MAX)) - .toList(); - } catch (Exception e) { - e.printStackTrace(); + for (int i = 0; i < cpuUsageList.length(); i++) { + JSONObject jsonObject = cpuUsageList.getJSONObject(i); + double maxValue = jsonObject.getDouble(KruizeConstants.JSONKeys.MAX); + cpuMaxValues.add(maxValue); } Double cpuRequest = 0.0;