Skip to content

Commit

Permalink
Merge pull request #1240 from shreyabiradar07/list-metric-profiles
Browse files Browse the repository at this point in the history
List metric profiles with name and verbose query parameters
  • Loading branch information
chandrams authored Jul 30, 2024
2 parents 34b7b11 + c7742cd commit 06f3968
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 53 deletions.
206 changes: 153 additions & 53 deletions src/main/java/com/autotune/analyzer/services/MetricProfileService.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@
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.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.gson.*;
import org.slf4j.Logger;
Expand All @@ -45,9 +48,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;

Expand Down Expand Up @@ -113,69 +114,103 @@ 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<String, PerformanceProfile> 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<String> 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<PerformanceProfile> 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<JsonNode>() {
@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
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<PerformanceProfile> values = metricProfilesMap.values();

// create Gson Object
Gson gsonObj = createGsonObject();

if (internalVerbose.equals("false")){
Collection<JsonObject> 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();
}

/**
Expand Down Expand Up @@ -246,4 +281,69 @@ public void sendErrorResponse(HttpServletResponse response, Exception e, int htt
}
response.sendError(httpStatusCode, errorMsg);
}

private void loadMetricProfilesFromCollection(Map<String, PerformanceProfile> 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<String, PerformanceProfile> 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<JsonNode>() {
@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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ public static final class PerformanceProfileConstants {
public static final String PERF_PROFILE_MAP = "performanceProfileMap";
public static final String METRIC_PROFILE_MAP = "metricProfileMap";
public static final String PERF_PROFILE_NAME = "name";
public static final String METRIC_PROFILE_NAME = "name";
public static final String OBJECTIVE_FUNCTION = "objectiveFunction";
public static final String FUNCTION_VARIABLES = "functionVariables";
public static final String VALUE_TYPE = "valueType";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 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 {
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/autotune/utils/KruizeSupportedTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,8 @@ private KruizeSupportedTypes() { }
public static final Set<String> QUERY_PARAMS_SUPPORTED = new HashSet<>(Arrays.asList(
"experiment_name", "results", "recommendations", "latest"
));

public static final Set<String> LIST_METRIC_PROFILES_QUERY_PARAMS_SUPPORTED = new HashSet<>(Arrays.asList(
"name", "verbose"
));
}

0 comments on commit 06f3968

Please sign in to comment.