Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update recommendations API optimalquery to fetch results #919

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ public boolean addResultsToLocalStorage(Map<String, KruizeObject> mainKruizeExpe
}
List<K8sObject> k8sObjectList = new ArrayList<>(k8sObjectHashMap.values());
ko.setKubernetes_objects(k8sObjectList);
LOGGER.debug("Added Results for Experiment name : {} with TimeStamp : {} into main map.", ko.getExperimentName(), resultData.getIntervalEndTime());
}
);
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,32 +81,15 @@ public List<KruizeRecommendationEngine> getEngines() {

@Override
public void generateRecommendation(KruizeObject kruizeObject, List<ExperimentResultData> experimentResultDataList, Timestamp interval_start_time, Timestamp interval_end_time) throws Exception {
/*
To restrict the number of rows in the result set, the Load results operation involves locating the appropriate method and configuring the desired limitation.
It's important to note that in order for the Limit rows feature to function correctly,
the CreateExperiment API must adhere strictly to the trail settings' measurement duration and should not allow arbitrary values
*/
String experiment_name = kruizeObject.getExperimentName();
int limitRows = (int) ((
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.LONG_TERM_DURATION_DAYS *
KruizeConstants.DateFormats.MINUTES_FOR_DAY)
/ kruizeObject.getTrial_settings().getMeasurement_durationMinutes_inDouble());

if (null != interval_start_time) {
long diffMilliseconds = interval_end_time.getTime() - interval_start_time.getTime();
long minutes = diffMilliseconds / (60 * 1000);
int addToLimitRows = (int) (minutes / kruizeObject.getTrial_settings().getMeasurement_durationMinutes_inDouble());
LOGGER.debug("add to limit rows set to {}", addToLimitRows);
limitRows = limitRows + addToLimitRows;
}
LOGGER.debug("Limit rows set to {}", limitRows);

String experiment_name = kruizeObject.getExperimentName();
Map<String, KruizeObject> mainKruizeExperimentMap = new HashMap<>();
mainKruizeExperimentMap.put(experiment_name, kruizeObject);
new ExperimentDBService().loadResultsFromDBByName(mainKruizeExperimentMap,
experiment_name,
interval_end_time,
limitRows);
interval_start_time,
interval_end_time
);
//TODO: Will be updated once algo is completed
for (ExperimentResultData experimentResultData : experimentResultDataList) {
if (null != kruizeObject && null != experimentResultData) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import io.micrometer.core.instrument.Timer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.micrometer.core.instrument.Timer;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
Expand Down Expand Up @@ -186,7 +185,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
sendErrorResponse(response, null, HttpServletResponse.SC_BAD_REQUEST, AnalyzerErrorConstants.APIErrors.UpdateRecommendationsAPI.DATA_NOT_FOUND);
return;
}
}catch (Exception e){
} catch (Exception e) {
LOGGER.error("Exception: " + e.getMessage());
e.printStackTrace();
sendErrorResponse(response, e, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
Expand All @@ -204,7 +203,6 @@ private void sendSuccessResponse(HttpServletResponse response, KruizeObject ko,
response.setStatus(HttpServletResponse.SC_CREATED);
List<ListRecommendationsAPIObject> recommendationList = new ArrayList<>();
try {
LOGGER.debug(ko.getKubernetes_objects().toString());
ListRecommendationsAPIObject listRecommendationsAPIObject = Converters.KruizeObjectConverters.
convertKruizeObjectToListRecommendationSO(
ko,
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/autotune/database/dao/ExperimentDAO.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ public interface ExperimentDAO {

List<KruizeResultsEntry> loadResultsByExperimentName(String experimentName, Timestamp interval_start_time, Integer limitRows) throws Exception;

int totalResultsByExperimentName(String experimentName, Timestamp interval_end_time) throws Exception;

List<KruizeResultsEntry> loadResultsByExperimentName(String experimentName, Timestamp from_start_time, Timestamp from_end_time, Integer limitRows) throws Exception;

// Load all recommendations of a particular experiment
List<KruizeRecommendationEntry> loadRecommendationsByExperimentName(String experimentName) throws Exception;

Expand Down
43 changes: 41 additions & 2 deletions src/main/java/com/autotune/database/dao/ExperimentDAOImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import org.slf4j.LoggerFactory;

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -396,6 +395,46 @@ public List<KruizeExperimentEntry> loadExperimentByName(String experimentName) t
return entries;
}

@Override
public List<KruizeResultsEntry> loadResultsByExperimentName(String experimentName, Timestamp from_start_time, Timestamp from_end_time, Integer limitRows) throws Exception {
List<KruizeResultsEntry> kruizeResultsEntries = null;
String statusValue = "failure";
Timer.Sample timerLoadResultsExpName = Timer.start(MetricsConfig.meterRegistry());
try (Session session = KruizeHibernateUtil.getSessionFactory().openSession()) {
kruizeResultsEntries = session.createQuery(DBConstants.SQLQUERY.SELECT_FROM_RESULTS_BY_EXP_NAME_AND_DATE_RANGE_AND_LIMIT, KruizeResultsEntry.class)
.setParameter(KruizeConstants.JSONKeys.EXPERIMENT_NAME, experimentName)
.setParameter(KruizeConstants.JSONKeys.INTERVAL_START_TIME, from_start_time)
.setParameter(KruizeConstants.JSONKeys.INTERVAL_END_TIME, from_end_time)
.setMaxResults(limitRows)
.list();
statusValue = "success";
} catch (Exception e) {
LOGGER.error("Not able to load results due to: {}", e.getMessage());
throw new Exception("Error while loading results from the database due to : " + e.getMessage());
} finally {
if (null != timerLoadResultsExpName) {
MetricsConfig.timerLoadResultsExpName = MetricsConfig.timerBLoadResultsExpName.tag("status", statusValue).register(MetricsConfig.meterRegistry());
timerLoadResultsExpName.stop(MetricsConfig.timerLoadResultsExpName);
}
}
return kruizeResultsEntries;
}

@Override
public int totalResultsByExperimentName(String experimentName, Timestamp interval_end_time) throws Exception {
Long count = 0L;
try (Session session = KruizeHibernateUtil.getSessionFactory().openSession()) {
Query<Long> query = session.createQuery(SELECT_FROM_RESULTS_BY_EXP_NAME_AND_GET_COUNT, Long.class);
query.setParameter(KruizeConstants.JSONKeys.EXPERIMENT_NAME, experimentName);
query.setParameter(KruizeConstants.JSONKeys.INTERVAL_END_TIME, interval_end_time);
count = query.getSingleResult();
} catch (Exception e) {
LOGGER.error("Not able to load results due to: {}", e.getMessage());
throw new Exception("Error while loading results from the database due to : " + e.getMessage());
}
return count.intValue();
}

@Override
public List<KruizeResultsEntry> loadResultsByExperimentName(String experimentName, Timestamp interval_end_time, Integer limitRows) throws Exception {
// TODO: load only experimentStatus=inProgress , playback may not require completed experiments
Expand All @@ -404,7 +443,7 @@ public List<KruizeResultsEntry> loadResultsByExperimentName(String experimentNam
Timer.Sample timerLoadResultsExpName = Timer.start(MetricsConfig.meterRegistry());
try (Session session = KruizeHibernateUtil.getSessionFactory().openSession()) {
if (null != limitRows && null != interval_end_time) {
kruizeResultsEntries = session.createQuery(DBConstants.SQLQUERY.SELECT_FROM_RESULTS_BY_EXP_NAME_AND_DATE_RANGE, KruizeResultsEntry.class)
kruizeResultsEntries = session.createQuery(DBConstants.SQLQUERY.SELECT_FROM_RESULTS_BY_EXP_NAME_AND_DATE_AND_LIMIT, KruizeResultsEntry.class)
.setParameter(KruizeConstants.JSONKeys.EXPERIMENT_NAME, experimentName)
.setParameter(KruizeConstants.JSONKeys.INTERVAL_END_TIME, interval_end_time)
.setMaxResults(limitRows)
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/com/autotune/database/helper/DBConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ public static final class SQLQUERY {
public static final String SELECT_FROM_EXPERIMENTS_BY_EXP_NAME = "from KruizeExperimentEntry k WHERE k.experiment_name = :experimentName";
public static final String SELECT_FROM_RESULTS = "from KruizeResultsEntry";
public static final String SELECT_FROM_RESULTS_BY_EXP_NAME = "from KruizeResultsEntry k WHERE k.experiment_name = :experimentName";
public static final String SELECT_FROM_RESULTS_BY_EXP_NAME_AND_DATE_RANGE = String.format("from KruizeResultsEntry k WHERE k.experiment_name = :%s and k.interval_end_time <= :%s ORDER BY k.interval_end_time DESC", KruizeConstants.JSONKeys.EXPERIMENT_NAME, KruizeConstants.JSONKeys.INTERVAL_END_TIME, KruizeConstants.JSONKeys.INTERVAL_START_TIME);
public static final String SELECT_FROM_RESULTS_BY_EXP_NAME_AND_DATE_AND_LIMIT = String.format("from KruizeResultsEntry k WHERE k.experiment_name = :%s and k.interval_end_time <= :%s ORDER BY k.interval_end_time DESC", KruizeConstants.JSONKeys.EXPERIMENT_NAME, KruizeConstants.JSONKeys.INTERVAL_END_TIME);

public static final String SELECT_FROM_RESULTS_BY_EXP_NAME_AND_GET_COUNT = String.format("select count(*) from KruizeResultsEntry k WHERE k.experiment_name = :%s and k.interval_end_time <= :%s", KruizeConstants.JSONKeys.EXPERIMENT_NAME, KruizeConstants.JSONKeys.INTERVAL_END_TIME);

public static final String SELECT_FROM_RESULTS_BY_EXP_NAME_AND_DATE_RANGE_AND_LIMIT = String.format("from KruizeResultsEntry k WHERE k.experiment_name = :%s and k.interval_start_time >= :%s and k.interval_end_time <= :%s ORDER BY k.interval_end_time DESC", KruizeConstants.JSONKeys.EXPERIMENT_NAME, KruizeConstants.JSONKeys.INTERVAL_START_TIME, KruizeConstants.JSONKeys.INTERVAL_END_TIME);
public static final String SELECT_FROM_RESULTS_BY_EXP_NAME_AND_START_END_TIME = String.format("from KruizeResultsEntry k WHERE k.experiment_name = :%s and k.interval_start_time >= :%s and k.interval_end_time <= :%s", KruizeConstants.JSONKeys.EXPERIMENT_NAME, KruizeConstants.JSONKeys.INTERVAL_START_TIME, KruizeConstants.JSONKeys.INTERVAL_END_TIME);
public static final String SELECT_FROM_RESULTS_BY_EXP_NAME_AND_END_TIME = String.format("from KruizeResultsEntry k WHERE k.experiment_name = :%s and k.interval_end_time = :%s", KruizeConstants.JSONKeys.EXPERIMENT_NAME, KruizeConstants.JSONKeys.INTERVAL_END_TIME);
public static final String SELECT_FROM_RESULTS_BY_EXP_NAME_AND_MAX_END_TIME = String.format("from KruizeResultsEntry k WHERE k.experiment_name = :%s and k.interval_end_time = (SELECT MAX(e.interval_end_time) FROM KruizeResultsEntry e where e.experiment_name = :%s )", KruizeConstants.JSONKeys.EXPERIMENT_NAME, KruizeConstants.JSONKeys.EXPERIMENT_NAME);
Expand Down
1 change: 0 additions & 1 deletion src/main/java/com/autotune/database/helper/DBHelpers.java
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,6 @@ public static KruizeRecommendationEntry convertKruizeObjectTORecommendation(Krui
if (null == listRecommendationsAPIObject) {
return null;
}
LOGGER.debug(new GsonBuilder().setPrettyPrinting().create().toJson(listRecommendationsAPIObject));
kruizeRecommendationEntry = new KruizeRecommendationEntry();
kruizeRecommendationEntry.setVersion(KruizeConstants.KRUIZE_RECOMMENDATION_API_VERSION.LATEST.getVersionNumber());
kruizeRecommendationEntry.setExperiment_name(listRecommendationsAPIObject.getExperimentName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,12 @@
import com.autotune.database.table.KruizeRecommendationEntry;
import com.autotune.database.table.KruizeResultsEntry;
import com.autotune.operator.KruizeOperator;
import com.autotune.utils.KruizeConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.*;

public class ExperimentDBService {
private static final long serialVersionUID = 1L;
Expand Down Expand Up @@ -142,10 +141,63 @@ public void loadAllPerformanceProfiles(Map<String, PerformanceProfile> performan
}
}

public void loadResultsFromDBByName(Map<String, KruizeObject> mainKruizeExperimentMap, String experimentName, Timestamp interval_end_time, Integer limitRows) throws Exception {
public void loadResultsFromDBByName(Map<String, KruizeObject> mainKruizeExperimentMap, String experimentName, Timestamp interval_start_time, Timestamp interval_end_time) throws Exception {
/*
The general strategy involves initially attempting the optimal query; if the sum of durations is not matched, then considering an alternative suboptimal query.
*/
// Convert the Timestamp to a Calendar instance in UTC time zone
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
Timestamp fromTime;
Timestamp toTime;
toTime = (interval_start_time == null) ? interval_end_time : interval_start_time;
cal.setTimeInMillis(toTime.getTime());
/*
* toTime Subtract (LONG_TERM_DURATION_DAYS + THRESHOLD days)
* Incorporate a buffer period of "threshold days" to account for potential remote cluster downtime.
* This adjustment aims to align the cumulative hours' duration with LONG_TERM_DURATION_DAYS.
* Additionally, there's an added layer of coverage involving the utilization of a LIMIT.
* This measure ensures that the total duration in hours corresponds precisely to LONG_TERM_DURATION_DAYS.
*/
cal.add(Calendar.DAY_OF_MONTH, -(KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.LONG_TERM_DURATION_DAYS +
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.LONG_TERM_DURATION_DAYS_THRESHOLD));
// Get the new Timestamp after subtracting 10 days
fromTime = new Timestamp(cal.getTimeInMillis());
/*
To restrict the number of rows in the result set, the Load results operation involves locating the appropriate method and configuring the desired limitation.
It's important to note that in order for the Limit rows feature to function correctly,
the CreateExperiment API must adhere strictly to the trail settings' measurement duration and should not allow arbitrary values
*/
KruizeObject kruizeObject = mainKruizeExperimentMap.get(experimentName);
int limitRows = (int) ((
KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.LONG_TERM_DURATION_DAYS *
KruizeConstants.DateFormats.MINUTES_FOR_DAY)
/ kruizeObject.getTrial_settings().getMeasurement_durationMinutes_inDouble());
LOGGER.debug("Limit rows set to {}", limitRows);
ExperimentInterface experimentInterface = new ExperimentInterfaceImpl();
// Load results from the DB and save to local
List<KruizeResultsEntry> kruizeResultsEntries = experimentDAO.loadResultsByExperimentName(experimentName, interval_end_time, limitRows);
List<KruizeResultsEntry> kruizeResultsEntries = experimentDAO.loadResultsByExperimentName(experimentName, fromTime, toTime, limitRows);
/*
Determine whether the sum of durations matches the value of the long-term duration.
If they do not match, execute a query that selects all rows with interval_end_times less than a specified threshold.
The selection should be limited based on the formula: (LongTermDuration_days * 60 mins) / trailsettings.duration_in_mins.
*/
double sum_of_duration_in_mins = 0.0;
for (KruizeResultsEntry entry : kruizeResultsEntries) {
sum_of_duration_in_mins += entry.getDuration_minutes();
}

if (sum_of_duration_in_mins < KruizeConstants.RecommendationEngineConstants.DurationBasedEngine.DurationAmount.LONG_TERM_DURATION_DAYS * KruizeConstants.DateFormats.MINUTES_FOR_DAY) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when we have more records than is needed. Since you are setting the threshold as 10 days, you can get significantly more records than needed.

int rowCount = experimentDAO.totalResultsByExperimentName(experimentName, interval_end_time);
if (rowCount > kruizeResultsEntries.size()) {
LOGGER.debug("The optimal query attempt was unsuccessful, prompting the exploration of an alternative suboptimal query.");
kruizeResultsEntries = experimentDAO.loadResultsByExperimentName(experimentName, interval_end_time, limitRows);
} else {
LOGGER.debug("The optimal query attempt successful");
}
} else {
LOGGER.debug("The optimal query attempt successful");
}

if (null != kruizeResultsEntries && !kruizeResultsEntries.isEmpty()) {
List<UpdateResultsAPIObject> updateResultsAPIObjects = DBHelpers.Converters.KruizeObjectConverters.convertResultEntryToUpdateResultsAPIObject(kruizeResultsEntries);
if (null != updateResultsAPIObjects && !updateResultsAPIObjects.isEmpty()) {
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/autotune/utils/KruizeConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,8 @@ public static final class DurationAmount {
public static final int MEDIUM_TERM_DURATION_DAYS = 7;
public static final int LONG_TERM_DURATION_DAYS = 15;

public static final int LONG_TERM_DURATION_DAYS_THRESHOLD = 10 ;

private DurationAmount() {

}
Expand Down
Loading