package com.linkedin.thirdeye.anomaly.detection.lib; import com.linkedin.thirdeye.anomaly.detection.DetectionJobScheduler; import com.linkedin.thirdeye.anomalydetection.performanceEvaluation.PerformanceEvaluate; import com.linkedin.thirdeye.anomalydetection.performanceEvaluation.PerformanceEvaluateHelper; import com.linkedin.thirdeye.anomalydetection.performanceEvaluation.PerformanceEvaluationMethod; import com.linkedin.thirdeye.dashboard.resources.OnboardResource; import com.linkedin.thirdeye.datalayer.bao.AnomalyFunctionManager; import com.linkedin.thirdeye.datalayer.bao.AutotuneConfigManager; import com.linkedin.thirdeye.datalayer.bao.MergedAnomalyResultManager; import com.linkedin.thirdeye.datalayer.bao.RawAnomalyResultManager; import com.linkedin.thirdeye.datalayer.dto.AnomalyFunctionDTO; import com.linkedin.thirdeye.datalayer.dto.AutotuneConfigDTO; import java.util.Map; import java.util.concurrent.TimeUnit; import org.joda.time.DateTime; import org.joda.time.Interval; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FunctionReplayRunnable implements Runnable { private static final Logger LOG = LoggerFactory.getLogger(FunctionReplayRunnable.class); private DetectionJobScheduler detectionJobScheduler; private MergedAnomalyResultManager mergedAnomalyResultDAO; private AnomalyFunctionManager anomalyFunctionDAO; private RawAnomalyResultManager rawAnomalyResultDAO; private AutotuneMethodType autotuneMethodType; private AutotuneConfigManager autotuneConfigDAO; private PerformanceEvaluationMethod performanceEvaluationMethod; private long tuningFunctionId; private DateTime replayStart; private DateTime replayEnd; private double goal; private boolean isForceBackfill; private Map<String, String> tuningParameter; private Long functionAutotuneConfigId; private boolean speedUp; private boolean selfKill; private long lastClonedFunctionId; public FunctionReplayRunnable(DetectionJobScheduler detectionJobScheduler, AnomalyFunctionManager anomalyFunctionDAO, MergedAnomalyResultManager mergedAnomalyResultDAO, RawAnomalyResultManager rawAnomalyResultDAO, AutotuneConfigManager autotuneConfigDAO){ this.detectionJobScheduler = detectionJobScheduler; this.mergedAnomalyResultDAO = mergedAnomalyResultDAO; this.anomalyFunctionDAO = anomalyFunctionDAO; this.rawAnomalyResultDAO = rawAnomalyResultDAO; this.autotuneConfigDAO = autotuneConfigDAO; setSpeedUp(true); setForceBackfill(true); setSelfKill(true); } public FunctionReplayRunnable(DetectionJobScheduler detectionJobScheduler, AnomalyFunctionManager anomalyFunctionDAO, MergedAnomalyResultManager mergedAnomalyResultDAO, RawAnomalyResultManager rawAnomalyResultDAO, AutotuneConfigManager autotuneConfigDAO, Map<String, String> tuningParameter, long tuningFunctionId, DateTime replayStart, DateTime replayEnd, double goal, long functionAutotuneConfigId, boolean isForceBackfill, boolean selfKill) { this(detectionJobScheduler, anomalyFunctionDAO, mergedAnomalyResultDAO, rawAnomalyResultDAO, autotuneConfigDAO); setTuningFunctionId(tuningFunctionId); setReplayStart(replayStart); setReplayEnd(replayEnd); setForceBackfill(isForceBackfill); setTuningParameter(tuningParameter); setFunctionAutotuneConfigId(functionAutotuneConfigId); setSpeedUp(true); setGoal(goal); setSelfKill(selfKill); } public FunctionReplayRunnable(DetectionJobScheduler detectionJobScheduler, AnomalyFunctionManager anomalyFunctionDAO, MergedAnomalyResultManager mergedAnomalyResultDAO, RawAnomalyResultManager rawAnomalyResultDAO, Map<String, String> tuningParameter, long tuningFunctionId, DateTime replayStart, DateTime replayEnd, boolean selfKill) { this(detectionJobScheduler, anomalyFunctionDAO, mergedAnomalyResultDAO, rawAnomalyResultDAO, null); setTuningFunctionId(tuningFunctionId); setReplayStart(replayStart); setReplayEnd(replayEnd); setForceBackfill(true); setTuningParameter(tuningParameter); setSpeedUp(true); setSelfKill(selfKill); } public static void speedup(AnomalyFunctionDTO anomalyFunctionDTO) { switch (anomalyFunctionDTO.getWindowUnit()) { case NANOSECONDS: case MICROSECONDS: case MILLISECONDS: case SECONDS: case MINUTES: // These TimeUnits are not currently in use case HOURS: case DAYS: /* SignTest takes HOURS data, but changing to 7 days won't affect the final result SPLINE takes 1 DAYS data, for heuristic, we extend it to 7 days. */ default: anomalyFunctionDTO.setWindowSize(7); anomalyFunctionDTO.setWindowUnit(TimeUnit.DAYS); anomalyFunctionDTO.setCron("0 0 0 ? * MON *"); } } @Override public void run() { long currentTime = System.currentTimeMillis(); long clonedFunctionId = 0l; OnboardResource onboardResource = new OnboardResource(anomalyFunctionDAO, mergedAnomalyResultDAO, rawAnomalyResultDAO); StringBuilder functionName = new StringBuilder("clone"); for (Map.Entry<String, String> entry : tuningParameter.entrySet()) { functionName.append("_"); functionName.append(entry.getKey()); functionName.append("_"); functionName.append(entry.getValue()); } try { clonedFunctionId = onboardResource.cloneAnomalyFunctionById(tuningFunctionId, functionName.toString(), false); this.lastClonedFunctionId = clonedFunctionId; } catch (Exception e) { LOG.error("Unable to clone function {} with given name {}", tuningFunctionId, functionName.toString(), e); return; } AnomalyFunctionDTO anomalyFunctionDTO = anomalyFunctionDAO.findById(clonedFunctionId); // Remove alert filters anomalyFunctionDTO.setAlertFilter(null); int originWindowSize = anomalyFunctionDTO.getWindowSize(); TimeUnit originWindowUnit = anomalyFunctionDTO.getWindowUnit(); String originCron = anomalyFunctionDTO.getCron(); // enlarge window size so that we can speed-up the replay speed if(speedUp) { FunctionReplayRunnable.speedup(anomalyFunctionDTO); } // Set Properties anomalyFunctionDTO.updateProperties(tuningParameter); anomalyFunctionDTO.setActive(true); anomalyFunctionDAO.update(anomalyFunctionDTO); detectionJobScheduler.synchronousBackFill(clonedFunctionId, replayStart, replayEnd, isForceBackfill); if(autotuneConfigDAO != null) { // if no functionAutotuneId, skip update PerformanceEvaluate performanceEvaluator = PerformanceEvaluateHelper.getPerformanceEvaluator(performanceEvaluationMethod, tuningFunctionId, clonedFunctionId, new Interval(replayStart.getMillis(), replayEnd.getMillis()), mergedAnomalyResultDAO); double performance = performanceEvaluator.evaluate(); AutotuneConfigDTO targetAutotuneDTO = autotuneConfigDAO.findById(functionAutotuneConfigId); Map<String, Double> prevPerformance = targetAutotuneDTO.getPerformance(); // if there is no previous performance, update performance directly // Otherwise, compare the performance, and update if betterW if (prevPerformance == null || prevPerformance.isEmpty() || Math.abs(prevPerformance.get(performanceEvaluationMethod.name()) - goal) > Math.abs(performance - goal)) { targetAutotuneDTO.setConfiguration(tuningParameter); Map<String, Double> newPerformance = targetAutotuneDTO.getPerformance(); newPerformance.put(performanceEvaluationMethod.name(), performance); targetAutotuneDTO.setPerformance(newPerformance); targetAutotuneDTO.setAvgRunningTime((System.currentTimeMillis() - currentTime) / 1000); targetAutotuneDTO.setLastUpdateTimestamp(System.currentTimeMillis()); } String message = (targetAutotuneDTO.getMessage().isEmpty()) ? "" : (targetAutotuneDTO.getMessage() + ";"); targetAutotuneDTO.setMessage(message + tuningParameter.toString() + ":" + performance); autotuneConfigDAO.update(targetAutotuneDTO); } // clean up and kill itself if(selfKill) { onboardResource.deleteExistingAnomalies(Long.toString(clonedFunctionId), replayStart.getMillis(), replayEnd.getMillis()); anomalyFunctionDAO.deleteById(clonedFunctionId); } else { anomalyFunctionDTO.setWindowSize(originWindowSize); anomalyFunctionDTO.setWindowUnit(originWindowUnit); anomalyFunctionDTO.setCron(originCron); anomalyFunctionDAO.update(anomalyFunctionDTO); } } public long getTuningFunctionId() { return tuningFunctionId; } public void setTuningFunctionId(long functionId) { this.tuningFunctionId = functionId; } public DateTime getReplayStart() { return replayStart; } public void setReplayStart(DateTime replayStart) { this.replayStart = replayStart; } public DateTime getReplayEnd() { return replayEnd; } public void setReplayEnd(DateTime replayEnd) { this.replayEnd = replayEnd; } public boolean isForceBackfill() { return isForceBackfill; } public void setForceBackfill(boolean forceBackfill) { isForceBackfill = forceBackfill; } public Map<String, String> getTuningParameter() { return tuningParameter; } public void setTuningParameter(Map<String, String> tuningParameter) { this.tuningParameter = tuningParameter; } public AutotuneMethodType getAutotuneMethodType() { return autotuneMethodType; } public void setAutotuneMethodType(AutotuneMethodType autotuneMethodType) { this.autotuneMethodType = autotuneMethodType; } public PerformanceEvaluationMethod getPerformanceEvaluationMethod() { return performanceEvaluationMethod; } public void setPerformanceEvaluationMethod(PerformanceEvaluationMethod performanceEvaluationMethod) { this.performanceEvaluationMethod = performanceEvaluationMethod; } public double getGoal() { return goal; } public void setGoal(double goal) { this.goal = goal; } public Long getFunctionAutotuneConfigId() { return functionAutotuneConfigId; } public void setFunctionAutotuneConfigId(Long functionAutotuneConfigId) { this.functionAutotuneConfigId = functionAutotuneConfigId; } public boolean isSpeedUp() { return speedUp; } public void setSpeedUp(boolean speedUp) { this.speedUp = speedUp; } public boolean isSelfKill() { return selfKill; } public void setSelfKill(boolean selfKill) { this.selfKill = selfKill; } public long getLastClonedFunctionId(){return lastClonedFunctionId;} }