diff --git a/src/main/java/com/autotune/analyzer/services/MetricProfileService.java b/src/main/java/com/autotune/analyzer/services/MetricProfileService.java index 7701cca17..1b5007dbf 100644 --- a/src/main/java/com/autotune/analyzer/services/MetricProfileService.java +++ b/src/main/java/com/autotune/analyzer/services/MetricProfileService.java @@ -27,8 +27,10 @@ import com.autotune.analyzer.utils.GsonUTCDateAdapter; import com.autotune.common.data.ValidationOutputData; import com.autotune.common.data.metrics.Metric; +import com.autotune.common.data.result.ContainerData; import com.autotune.database.service.ExperimentDBService; import com.autotune.utils.KruizeConstants; +import com.autotune.utils.KruizeSupportedTypes; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.gson.*; @@ -45,9 +47,7 @@ import java.io.PrintWriter; import java.io.Serial; import java.lang.reflect.Type; -import java.util.Collection; -import java.util.Date; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -112,69 +112,102 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) /** * Get List of Metric Profiles * - * @param req + * @param request * @param response * @throws ServletException * @throws IOException */ @Override - protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException { + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType(JSON_CONTENT_TYPE); response.setCharacterEncoding(CHARACTER_ENCODING); response.setStatus(HttpServletResponse.SC_OK); String gsonStr = "[]"; - // Fetch all metric profiles from the DB - try { - new ExperimentDBService().loadAllMetricProfiles(metricProfilesMap); - } catch (Exception e) { - LOGGER.error(KruizeConstants.MetricProfileAPIMessages.LOAD_METRIC_PROFILE_FAILURE, e.getMessage()); + + ConcurrentHashMap metricProfilesMap = new ConcurrentHashMap<>(); + String metricProfileName = request.getParameter(AnalyzerConstants.PerformanceProfileConstants.METRIC_PROFILE_NAME); + String verbose = request.getParameter(AnalyzerConstants.ServiceConstants.VERBOSE); + String internalVerbose = "false"; + boolean error = false; + + // validate Query params + Set invalidParams = new HashSet<>(); + for (String param : request.getParameterMap().keySet()) { + if (!KruizeSupportedTypes.LIST_METRIC_PROFILES_QUERY_PARAMS_SUPPORTED.contains(param)) { + invalidParams.add(param); + } } - if (metricProfilesMap.size() > 0) { - Collection values = metricProfilesMap.values(); - Gson gsonObj = new GsonBuilder() - .disableHtmlEscaping() - .setPrettyPrinting() - .enableComplexMapKeySerialization() - .registerTypeAdapter(Date.class, new GsonUTCDateAdapter()) - // a custom serializer for serializing metadata of JsonNode type. - .registerTypeAdapter(JsonNode.class, new JsonSerializer() { - @Override - public JsonElement serialize(JsonNode jsonNode, Type typeOfSrc, JsonSerializationContext context) { - if (jsonNode instanceof ObjectNode) { - ObjectNode objectNode = (ObjectNode) jsonNode; - JsonObject metadataJson = new JsonObject(); - - // Extract the "name" field directly if it exists - if (objectNode.has("name")) { - metadataJson.addProperty("name", objectNode.get("name").asText()); - } - - return metadataJson; - } - return context.serialize(jsonNode); - } - }) - .setExclusionStrategies(new ExclusionStrategy() { - @Override - public boolean shouldSkipField(FieldAttributes f) { - return f.getDeclaringClass() == Metric.class && ( - f.getName().equals("trialSummaryResult") - || f.getName().equals("cycleDataMap") - ); - } - @Override - public boolean shouldSkipClass(Class aClass) { - return false; + // Fetch metric profiles based on the query parameters using the in-memory storage collection + try { + if (invalidParams.isEmpty()) { + if (null != verbose) { + internalVerbose = verbose; + } + + try { + if (null != metricProfileName && !metricProfileName.isEmpty()) { + internalVerbose = "true"; + loadMetricProfilesFromCollection(metricProfilesMap, metricProfileName); + } else { + loadAllMetricProfilesFromCollection(metricProfilesMap); + } + + // Check if metric profile exists + if (metricProfileName != null && !metricProfilesMap.containsKey(metricProfileName)) { + error = true; + sendErrorResponse( + response, + new Exception(AnalyzerErrorConstants.APIErrors.ListMetricProfileAPI.INVALID_METRIC_PROFILE_NAME_EXCPTN), + HttpServletResponse.SC_BAD_REQUEST, + String.format(AnalyzerErrorConstants.APIErrors.ListMetricProfileAPI.INVALID_METRIC_PROFILE_NAME_MSG, metricProfileName) + ); + } else if (null == metricProfileName && metricProfilesMap.isEmpty()) { + error = true; + sendErrorResponse( + response, + new Exception(AnalyzerErrorConstants.APIErrors.ListMetricProfileAPI.NO_METRIC_PROFILES_EXCPTN), + HttpServletResponse.SC_BAD_REQUEST, + AnalyzerErrorConstants.APIErrors.ListMetricProfileAPI.NO_METRIC_PROFILES + ); + } + + if (!error) { + Collection values = metricProfilesMap.values(); + // create Gson Object + Gson gsonObj = createGsonObject(); + + if (internalVerbose.equals("false")) { + Collection filteredValues = new ArrayList<>(); + for(PerformanceProfile performanceProfile: values) { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("name", performanceProfile.getMetadata().get("name").asText()); + filteredValues.add(jsonObject); + } + gsonStr = gsonObj.toJson(filteredValues); + } else { + gsonStr = gsonObj.toJson(values); } - }) - .create(); - gsonStr = gsonObj.toJson(values); - } else { - LOGGER.debug(AnalyzerErrorConstants.AutotuneObjectErrors.NO_PERF_PROFILE); + response.getWriter().println(gsonStr); + response.getWriter().close(); + } + } catch (Exception e) { + LOGGER.error("Exception: {}", e.getMessage()); + e.printStackTrace(); + sendErrorResponse(response, e, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + } else { + sendErrorResponse( + response, + new Exception(AnalyzerErrorConstants.APIErrors.ListMetricProfileAPI.INVALID_QUERY_PARAM), + HttpServletResponse.SC_BAD_REQUEST, + String.format(AnalyzerErrorConstants.APIErrors.ListMetricProfileAPI.INVALID_QUERY_PARAM, invalidParams) + ); + } + } catch (Exception e) { + LOGGER.error(KruizeConstants.MetricProfileAPIMessages.LOAD_METRIC_PROFILE_FAILURE, e.getMessage()); + sendErrorResponse(response, e, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } - response.getWriter().println(gsonStr); - response.getWriter().close(); } /** @@ -245,4 +278,69 @@ public void sendErrorResponse(HttpServletResponse response, Exception e, int htt } response.sendError(httpStatusCode, errorMsg); } + + private void loadMetricProfilesFromCollection(Map metricProfilesMap, String metricProfileName) { + try { + MetricProfileCollection metricProfileCollection = MetricProfileCollection.getInstance(); + if (null != metricProfileName && !metricProfileName.isEmpty()) { + PerformanceProfile metricProfile = metricProfileCollection.getMetricProfileCollection().get(metricProfileName); + metricProfilesMap.put(metricProfileName, metricProfile); + } + } catch (Exception e) { + LOGGER.error("Failed to load saved metric profile data: {} ", e.getMessage()); + } + } + + private void loadAllMetricProfilesFromCollection(Map metricProfilesMap) { + try { + MetricProfileCollection metricProfileCollection = MetricProfileCollection.getInstance(); + metricProfilesMap.putAll(metricProfileCollection.getMetricProfileCollection()); + } catch (Exception e) { + LOGGER.error("Failed to load all the metric profiles data: {} ", e.getMessage()); + } + } + + private Gson createGsonObject() { + return new GsonBuilder() + .disableHtmlEscaping() + .setPrettyPrinting() + .enableComplexMapKeySerialization() + .registerTypeAdapter(Date.class, new GsonUTCDateAdapter()) + // a custom serializer for serializing metadata of JsonNode type. + .registerTypeAdapter(JsonNode.class, new JsonSerializer() { + @Override + public JsonElement serialize(JsonNode jsonNode, Type typeOfSrc, JsonSerializationContext context) { + if (jsonNode instanceof ObjectNode) { + ObjectNode objectNode = (ObjectNode) jsonNode; + JsonObject metadataJson = new JsonObject(); + + // Extract the "name" field directly if it exists + if (objectNode.has("name")) { + metadataJson.addProperty("name", objectNode.get("name").asText()); + } + + return metadataJson; + } + return context.serialize(jsonNode); + } + }) + .setExclusionStrategies(new ExclusionStrategy() { + @Override + public boolean shouldSkipField(FieldAttributes f) { + return f.getDeclaringClass() == Metric.class && ( + f.getName().equals("trialSummaryResult") + || f.getName().equals("cycleDataMap") + ) || + f.getDeclaringClass() == ContainerData.class && ( + f.getName().equalsIgnoreCase("metrics") + ); + } + + @Override + public boolean shouldSkipClass(Class aClass) { + return false; + } + }) + .create(); + } } diff --git a/src/main/java/com/autotune/analyzer/utils/AnalyzerConstants.java b/src/main/java/com/autotune/analyzer/utils/AnalyzerConstants.java index a8677da1b..10d48cbfb 100644 --- a/src/main/java/com/autotune/analyzer/utils/AnalyzerConstants.java +++ b/src/main/java/com/autotune/analyzer/utils/AnalyzerConstants.java @@ -457,6 +457,7 @@ public static final class PerformanceProfileConstants { public static final String PERF_PROFILE_NAME = "name"; public static final String OBJECTIVE_FUNCTION = "objectiveFunction"; public static final String FUNCTION_VARIABLES = "functionVariables"; + public static final String METRIC_PROFILE_NAME = "name"; public static final String VALUE_TYPE = "valueType"; public static final String SOURCE = "source"; public static final String PERFORMANCE_PROFILE_PKG = "com.autotune.analyzer.performanceProfiles.PerformanceProfileInterface."; diff --git a/src/main/java/com/autotune/analyzer/utils/AnalyzerErrorConstants.java b/src/main/java/com/autotune/analyzer/utils/AnalyzerErrorConstants.java index 791223401..015c9b03b 100644 --- a/src/main/java/com/autotune/analyzer/utils/AnalyzerErrorConstants.java +++ b/src/main/java/com/autotune/analyzer/utils/AnalyzerErrorConstants.java @@ -223,6 +223,17 @@ private DSMetadataAPI(){ public static final String DATASOURCE_METADATA_MISSING_REQUEST_INPUT_EXCPTN = "Request input data cannot be null or empty"; public static final String DATASOURCE_METADATA_CONNECTION_FAILED = "Metadata cannot be imported, datasource connection refused or timed out"; } + + public static final class ListMetricProfileAPI { + public ListMetricProfileAPI() { + } + public static final String INVALID_QUERY_PARAM = "The query param(s) - %s is/are invalid"; + public static final String INVALID_QUERY_PARAM_VALUE = "The query param value(s) is/are invalid"; + public static final String INVALID_METRIC_PROFILE_NAME_EXCPTN = "Invalid Metric Profile Name"; + public static final String INVALID_METRIC_PROFILE_NAME_MSG = "Given metric profile name - %s either does not exist or is not valid"; + public static final String NO_METRIC_PROFILES_EXCPTN = "No metric profile"; + public static final String NO_METRIC_PROFILES = "No metric profiles found!"; + } } public static final class ConversionErrors { diff --git a/src/main/java/com/autotune/utils/KruizeSupportedTypes.java b/src/main/java/com/autotune/utils/KruizeSupportedTypes.java index 135cd1127..d297efd2f 100644 --- a/src/main/java/com/autotune/utils/KruizeSupportedTypes.java +++ b/src/main/java/com/autotune/utils/KruizeSupportedTypes.java @@ -81,10 +81,14 @@ private KruizeSupportedTypes() { } "datasource", "cluster_name", "namespace", "verbose" )); - public static final Set SUPPORTED_FORMATS = + public static final Set SUPPORTED_FORMATS = new HashSet<>(Arrays.asList("cores", "m", "Bytes", "bytes", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "kB", "KB", "MB", "GB", "TB", "PB", "EB", "K", "k", "M", "G", "T", "P", "E")); public static final Set QUERY_PARAMS_SUPPORTED = new HashSet<>(Arrays.asList( "experiment_name", "results", "recommendations", "latest" )); + + public static final Set LIST_METRIC_PROFILES_QUERY_PARAMS_SUPPORTED = new HashSet<>(Arrays.asList( + "name", "verbose" + )); }