Skip to content

Commit

Permalink
Merge pull request kruize#1421 from msvinaykumar/cft-6-ListExperiments
Browse files Browse the repository at this point in the history
listExperiments fix for concurrent rm and lm
  • Loading branch information
chandrams authored Dec 17, 2024
2 parents 00e0ebd + f2f1d48 commit 5473b36
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 82 deletions.
52 changes: 40 additions & 12 deletions src/main/java/com/autotune/analyzer/services/ListExperiments.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,19 +107,27 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t
String latest = request.getParameter(LATEST);
String recommendations = request.getParameter(KruizeConstants.JSONKeys.RECOMMENDATIONS);
String experimentName = request.getParameter(EXPERIMENT_NAME);
String rm = request.getParameter(AnalyzerConstants.ServiceConstants.RM);
String requestBody = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
StringBuilder clusterName = new StringBuilder();
List<KubernetesAPIObject> kubernetesAPIObjectList = new ArrayList<>();
boolean isJSONValid = true;
Map<String, KruizeObject> mKruizeExperimentMap = new ConcurrentHashMap<>();
boolean error = false;
boolean rmTable = false;
// validate Query params
Set<String> invalidParams = new HashSet<>();
for (String param : request.getParameterMap().keySet()) {
if (!KruizeSupportedTypes.QUERY_PARAMS_SUPPORTED.contains(param)) {
invalidParams.add(param);
}
}
if (null != rm
&& !rm.isEmpty()
&& rm.equalsIgnoreCase(AnalyzerConstants.BooleanString.TRUE)
) {
rmTable = true;
}
try {
if (invalidParams.isEmpty()) {
// Set default values if absent
Expand All @@ -142,13 +150,21 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t
// parse the requestBody JSON into corresponding classes
parseInputJSON(requestBody, clusterName, kubernetesAPIObjectList);
try {
new ExperimentDBService().loadExperimentFromDBByInputJSON(mKruizeExperimentMap, clusterName, kubernetesAPIObjectList);
if (rmTable)
new ExperimentDBService().loadExperimentFromDBByInputJSON(mKruizeExperimentMap, clusterName, kubernetesAPIObjectList);
else {
new ExperimentDBService().loadLMExperimentFromDBByInputJSON(mKruizeExperimentMap, clusterName, kubernetesAPIObjectList);
}
} catch (Exception e) {
LOGGER.error("Failed to load saved experiment data: {} ", e.getMessage());
}
} else {
// Fetch experiments data from the DB and check if the requested experiment exists
loadExperimentsFromDatabase(mKruizeExperimentMap, experimentName);
if (rmTable) {
loadExperimentsFromDatabase(mKruizeExperimentMap, experimentName);
} else {
loadLMExperimentsFromDatabase(mKruizeExperimentMap, experimentName);
}
}
// Check if experiment exists
if (experimentName != null && !mKruizeExperimentMap.containsKey(experimentName)) {
Expand All @@ -161,18 +177,18 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t
);
}
if (!error) {
// create Gson Object
Gson gsonObj = createGsonObject();
// create Gson Object
Gson gsonObj = createGsonObject();

// Modify the JSON response here based on query params.
gsonStr = buildResponseBasedOnQuery(mKruizeExperimentMap, gsonObj, results, recommendations, latest, experimentName);
if (gsonStr.isEmpty()) {
gsonStr = generateDefaultResponse();
}
response.getWriter().println(gsonStr);
response.getWriter().close();
statusValue = "success";
// Modify the JSON response here based on query params.
gsonStr = buildResponseBasedOnQuery(mKruizeExperimentMap, gsonObj, results, recommendations, latest, experimentName);
if (gsonStr.isEmpty()) {
gsonStr = generateDefaultResponse();
}
response.getWriter().println(gsonStr);
response.getWriter().close();
statusValue = "success";
}
} catch (Exception e) {
LOGGER.error("Exception: " + e.getMessage());
e.printStackTrace();
Expand Down Expand Up @@ -278,6 +294,18 @@ private void loadExperimentsFromDatabase(Map<String, KruizeObject> mKruizeExperi
}
}

private void loadLMExperimentsFromDatabase(Map<String, KruizeObject> mKruizeExperimentMap, String experimentName) {
try {
if (experimentName == null || experimentName.isEmpty())
new ExperimentDBService().loadAllLMExperiments(mKruizeExperimentMap);
else
new ExperimentDBService().loadLMExperimentFromDBByName(mKruizeExperimentMap, experimentName);

} catch (Exception e) {
LOGGER.error("Failed to load saved experiment data: {} ", e.getMessage());
}
}

private Gson createGsonObject() {
return new GsonBuilder()
.disableHtmlEscaping()
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/com/autotune/database/dao/ExperimentDAO.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public interface ExperimentDAO {
// If Kruize object restarts load all experiment which are in inprogress
public List<KruizeExperimentEntry> loadAllExperiments() throws Exception;

// If Kruize object restarts load all local monitoring experiments which are in inprogress

public List<KruizeLMExperimentEntry> loadAllLMExperiments() throws Exception;

// If Kruize object restarts load all results from the experiments which are in inprogress
Expand Down Expand Up @@ -108,6 +108,8 @@ public interface ExperimentDAO {

List<KruizeExperimentEntry> loadExperimentFromDBByInputJSON(StringBuilder clusterName, KubernetesAPIObject kubernetesAPIObject) throws Exception;

List<KruizeLMExperimentEntry> loadLMExperimentFromDBByInputJSON(StringBuilder clusterName, KubernetesAPIObject kubernetesAPIObject) throws Exception;

// Load all the datasources
List<KruizeDataSourceEntry> loadAllDataSources() throws Exception;

Expand Down
32 changes: 32 additions & 0 deletions src/main/java/com/autotune/database/dao/ExperimentDAOImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,38 @@ public List<KruizeExperimentEntry> loadExperimentFromDBByInputJSON(StringBuilder
return entries;
}

@Override
public List<KruizeLMExperimentEntry> loadLMExperimentFromDBByInputJSON(StringBuilder clusterName, KubernetesAPIObject kubernetesAPIObject) throws Exception {
//todo load only experimentStatus=inprogress , playback may not require completed experiments
List<KruizeLMExperimentEntry> entries;
String statusValue = "failure";
Timer.Sample timerLoadExpName = Timer.start(MetricsConfig.meterRegistry());
try (Session session = KruizeHibernateUtil.getSessionFactory().openSession()) {
// assuming there will be only one container
ContainerAPIObject containerAPIObject = kubernetesAPIObject.getContainerAPIObjects().get(0);
// Set parameters for KubernetesObject and Container
Query<KruizeLMExperimentEntry> query = session.createNativeQuery(SELECT_FROM_LM_EXPERIMENTS_BY_INPUT_JSON, KruizeLMExperimentEntry.class);
query.setParameter(CLUSTER_NAME, clusterName.toString());
query.setParameter(KruizeConstants.JSONKeys.NAME, kubernetesAPIObject.getName());
query.setParameter(KruizeConstants.JSONKeys.NAMESPACE, kubernetesAPIObject.getNamespace());
query.setParameter(KruizeConstants.JSONKeys.TYPE, kubernetesAPIObject.getType());
query.setParameter(KruizeConstants.JSONKeys.CONTAINER_NAME, containerAPIObject.getContainer_name());
query.setParameter(KruizeConstants.JSONKeys.CONTAINER_IMAGE_NAME, containerAPIObject.getContainer_image_name());

entries = query.getResultList();
statusValue = "success";
} catch (Exception e) {
LOGGER.error("Error fetching experiment data: {}", e.getMessage());
throw new Exception("Error while fetching experiment data from database: " + e.getMessage());
} finally {
if (null != timerLoadExpName) {
MetricsConfig.timerLoadExpName = MetricsConfig.timerBLoadExpName.tag("status", statusValue).register(MetricsConfig.meterRegistry());
timerLoadExpName.stop(MetricsConfig.timerLoadExpName);
}
}
return entries;
}


@Override
public List<KruizeResultsEntry> loadResultsByExperimentName(String experimentName, String cluster_name, Timestamp calculated_start_time, Timestamp interval_end_time) throws Exception {
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/com/autotune/database/helper/DBConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ public static final class SQLQUERY {
" WHERE container->>'container_name' = :container_name" +
" AND container->>'container_image_name' = :container_image_name" +
" ))";
public static final String SELECT_FROM_LM_EXPERIMENTS_BY_INPUT_JSON = "SELECT * FROM kruize_lm_experiments WHERE cluster_name = :cluster_name " +
"AND EXISTS (SELECT 1 FROM jsonb_array_elements(extended_data->'kubernetes_objects') AS kubernetes_object" +
" WHERE kubernetes_object->>'name' = :name " +
" AND kubernetes_object->>'namespace' = :namespace " +
" AND kubernetes_object->>'type' = :type " +
" AND EXISTS (SELECT 1 FROM jsonb_array_elements(kubernetes_object->'containers') AS container" +
" WHERE container->>'container_name' = :container_name" +
" AND container->>'container_image_name' = :container_image_name" +
" ))";
}

public static final class TABLE_NAMES {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,26 @@ public void loadExperimentFromDBByInputJSON(Map<String, KruizeObject> mKruizeExp
}
}

public void loadLMExperimentFromDBByInputJSON(Map<String, KruizeObject> mKruizeExperimentMap, StringBuilder clusterName, List<KubernetesAPIObject> kubernetesAPIObjectList) throws Exception {
ExperimentInterface experimentInterface = new ExperimentInterfaceImpl();
// assuming there will be only one Kubernetes object
KubernetesAPIObject kubernetesAPIObject = kubernetesAPIObjectList.get(0);
List<KruizeLMExperimentEntry> entries = experimentDAO.loadLMExperimentFromDBByInputJSON(clusterName, kubernetesAPIObject);
if (null != entries && !entries.isEmpty()) {
List<CreateExperimentAPIObject> createExperimentAPIObjects = DBHelpers.Converters.KruizeObjectConverters.convertLMExperimentEntryToCreateExperimentAPIObject(entries);
if (!createExperimentAPIObjects.isEmpty()) {
List<KruizeObject> kruizeExpList = new ArrayList<>();
for (CreateExperimentAPIObject createExperimentAPIObject : createExperimentAPIObjects) {
KruizeObject kruizeObject = Converters.KruizeObjectConverters.convertCreateExperimentAPIObjToKruizeObject(createExperimentAPIObject);
if (null != kruizeObject) {
kruizeExpList.add(kruizeObject);
}
}
experimentInterface.addExperimentToLocalStorage(mKruizeExperimentMap, kruizeExpList);
}
}
}

public void loadExperimentAndResultsFromDBByName(Map<String, KruizeObject> mainKruizeExperimentMap, String experimentName) throws Exception {

loadExperimentFromDBByName(mainKruizeExperimentMap, experimentName);
Expand Down
119 changes: 50 additions & 69 deletions src/main/java/com/autotune/utils/KruizeSupportedTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,73 +22,54 @@
/**
* Supported types to both Autotune and KruizeLayer objects
*/
public class KruizeSupportedTypes
{
private KruizeSupportedTypes() { }

public static final Set<String> DIRECTIONS_SUPPORTED =
new HashSet<>(Arrays.asList("minimize", "maximize"));

public static final Set<String> MONITORING_AGENTS_SUPPORTED =
new HashSet<>(Arrays.asList("prometheus"));

public static final Set<String> MODES_SUPPORTED =
new HashSet<>(Arrays.asList("experiment", "monitor"));

public static final Set<String> TARGET_CLUSTERS_SUPPORTED =
new HashSet<>(Arrays.asList("local", "remote"));

public static final Set<String> PRESENCE_SUPPORTED =
new HashSet<>(Arrays.asList("always", "", null));

public static final Set<String> SLO_CLASSES_SUPPORTED =
new HashSet<>(Arrays.asList("throughput", "response_time", "resource_usage"));

public static final Set<String> LAYERS_SUPPORTED =
new HashSet<>(Arrays.asList("container", "hotspot", "quarkus"));

public static final Set<String> VALUE_TYPES_SUPPORTED =
new HashSet<>(Arrays.asList("double", "int", "string", "categorical"));

public static final Set<String> CLUSTER_TYPES_SUPPORTED =
new HashSet<>(Arrays.asList("kubernetes"));

public static final Set<String> K8S_TYPES_SUPPORTED =
new HashSet<>(Arrays.asList("minikube", "openshift", "icp", null));

public static final Set<String> AUTH_TYPES_SUPPORTED =
new HashSet<>(Arrays.asList("saml", "oidc", "", null));

public static final Set<String> LOGGING_TYPES_SUPPORTED =
new HashSet<>(Arrays.asList("all", "debug", "error", "info", "off", "warn"));

public static final Set<String> HPO_ALGOS_SUPPORTED =
new HashSet<>(Arrays.asList("optuna_tpe", "optuna_tpe_multivariate", "optuna_skopt", null));

public static final Set<String> MATH_OPERATORS_SUPPORTED =
new HashSet<>(Arrays.asList("+", "-", "*", "/", "^","%","sin", "cos", "tan", "log"));

public static final Set<String> OBJECTIVE_FUNCTION_LIST =
new HashSet<>(Arrays.asList("(( throughput / transaction_response_time) / max_response_time) * 100",
"request_sum/request_count",
"(1.25 * request_count) - (1.5 * (request_sum / request_count)) - (0.25 * request_max)",
"((request_count / (request_sum / request_count)) / request_max) * 100"));

public static final Set<String> KUBERNETES_OBJECTS_SUPPORTED =
new HashSet<>(Arrays.asList("deployment", "pod", "container", "namespace"));

public static final Set<String> DSMETADATA_QUERY_PARAMS_SUPPORTED = new HashSet<>(Arrays.asList(
"datasource", "cluster_name", "namespace", "verbose"
));

public static final Set<String> 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<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"
));
public class KruizeSupportedTypes {
public static final Set<String> DIRECTIONS_SUPPORTED =
new HashSet<>(Arrays.asList("minimize", "maximize"));
public static final Set<String> MONITORING_AGENTS_SUPPORTED =
new HashSet<>(Arrays.asList("prometheus"));
public static final Set<String> MODES_SUPPORTED =
new HashSet<>(Arrays.asList("experiment", "monitor"));
public static final Set<String> TARGET_CLUSTERS_SUPPORTED =
new HashSet<>(Arrays.asList("local", "remote"));
public static final Set<String> PRESENCE_SUPPORTED =
new HashSet<>(Arrays.asList("always", "", null));
public static final Set<String> SLO_CLASSES_SUPPORTED =
new HashSet<>(Arrays.asList("throughput", "response_time", "resource_usage"));
public static final Set<String> LAYERS_SUPPORTED =
new HashSet<>(Arrays.asList("container", "hotspot", "quarkus"));
public static final Set<String> VALUE_TYPES_SUPPORTED =
new HashSet<>(Arrays.asList("double", "int", "string", "categorical"));
public static final Set<String> CLUSTER_TYPES_SUPPORTED =
new HashSet<>(Arrays.asList("kubernetes"));
public static final Set<String> K8S_TYPES_SUPPORTED =
new HashSet<>(Arrays.asList("minikube", "openshift", "icp", null));
public static final Set<String> AUTH_TYPES_SUPPORTED =
new HashSet<>(Arrays.asList("saml", "oidc", "", null));
public static final Set<String> LOGGING_TYPES_SUPPORTED =
new HashSet<>(Arrays.asList("all", "debug", "error", "info", "off", "warn"));
public static final Set<String> HPO_ALGOS_SUPPORTED =
new HashSet<>(Arrays.asList("optuna_tpe", "optuna_tpe_multivariate", "optuna_skopt", null));
public static final Set<String> MATH_OPERATORS_SUPPORTED =
new HashSet<>(Arrays.asList("+", "-", "*", "/", "^", "%", "sin", "cos", "tan", "log"));
public static final Set<String> OBJECTIVE_FUNCTION_LIST =
new HashSet<>(Arrays.asList("(( throughput / transaction_response_time) / max_response_time) * 100",
"request_sum/request_count",
"(1.25 * request_count) - (1.5 * (request_sum / request_count)) - (0.25 * request_max)",
"((request_count / (request_sum / request_count)) / request_max) * 100"));
public static final Set<String> KUBERNETES_OBJECTS_SUPPORTED =
new HashSet<>(Arrays.asList("deployment", "pod", "container", "namespace"));
public static final Set<String> DSMETADATA_QUERY_PARAMS_SUPPORTED = new HashSet<>(Arrays.asList(
"datasource", "cluster_name", "namespace", "verbose"
));
public static final Set<String> 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<String> QUERY_PARAMS_SUPPORTED = new HashSet<>(Arrays.asList(
"experiment_name", "results", "recommendations", "latest", "rm"
));
public static final Set<String> LIST_METRIC_PROFILES_QUERY_PARAMS_SUPPORTED = new HashSet<>(Arrays.asList(
"name", "verbose"
));

private KruizeSupportedTypes() {
}
}

0 comments on commit 5473b36

Please sign in to comment.