package com.linkedin.thirdeye.anomaly.alert;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.joda.time.DateTime;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.linkedin.thirdeye.anomaly.job.JobConstants.JobStatus;
import com.linkedin.thirdeye.anomaly.task.TaskConstants.TaskStatus;
import com.linkedin.thirdeye.anomaly.task.TaskConstants.TaskType;
import com.linkedin.thirdeye.anomaly.task.TaskGenerator;
import com.linkedin.thirdeye.datalayer.bao.EmailConfigurationManager;
import com.linkedin.thirdeye.datalayer.bao.JobManager;
import com.linkedin.thirdeye.datalayer.bao.TaskManager;
import com.linkedin.thirdeye.datalayer.dto.EmailConfigurationDTO;
import com.linkedin.thirdeye.datalayer.dto.JobDTO;
import com.linkedin.thirdeye.datalayer.dto.TaskDTO;
public class AlertJobRunner implements Job {
private static final Logger LOG = LoggerFactory.getLogger(AlertJobRunner.class);
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
public static final String ALERT_JOB_CONTEXT = "ALERT_JOB_CONTEXT";
public static final String ALERT_JOB_MONITORING_WINDOW_START_TIME = "ALERT_JOB_MONITORING_WINDOW_START_TIME";
public static final String ALERT_JOB_MONITORING_WINDOW_END_TIME = "ALERT_JOB_MONITORING_WINDOW_END_TIME";
private JobManager jobDAO;
private TaskManager taskDAO;
private AlertJobContext alertJobContext;
private TaskGenerator taskGenerator;
public AlertJobRunner() {
taskGenerator = new TaskGenerator();
}
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
LOG.info("Running " + jobExecutionContext.getJobDetail().getKey().toString());
alertJobContext = (AlertJobContext) jobExecutionContext.getJobDetail().getJobDataMap()
.get(ALERT_JOB_CONTEXT);
jobDAO = alertJobContext.getJobDAO();
taskDAO = alertJobContext.getTaskDAO();
EmailConfigurationManager emailConfigurationDAO = alertJobContext.getEmailConfigurationDAO();
long alertConfigId = alertJobContext.getAlertConfigId();
EmailConfigurationDTO alertConfig = emailConfigurationDAO.findById(alertConfigId);
if (alertConfig == null) {
LOG.error("Alert config with id {} does not exist", alertConfigId);
} else {
alertJobContext.setAlertConfig(alertConfig);
DateTime monitoringWindowStartTime =
(DateTime) jobExecutionContext.getJobDetail().getJobDataMap().get(ALERT_JOB_MONITORING_WINDOW_START_TIME);
DateTime monitoringWindowEndTime =
(DateTime) jobExecutionContext.getJobDetail().getJobDataMap().get(ALERT_JOB_MONITORING_WINDOW_END_TIME);
// Compute window end
if (monitoringWindowEndTime == null) {
long delayMillis = 0;
if (alertConfig.getWindowDelay() != null) {
delayMillis = TimeUnit.MILLISECONDS.convert(alertConfig.getWindowDelay(),
alertConfig.getWindowDelayUnit());
}
Date scheduledFireTime = jobExecutionContext.getScheduledFireTime();
monitoringWindowEndTime = new DateTime(scheduledFireTime).minus(delayMillis);
}
// Compute window start according to window end
if (monitoringWindowStartTime == null) {
int windowSize = alertConfig.getWindowSize();
TimeUnit windowUnit = alertConfig.getWindowUnit();
long windowMillis = TimeUnit.MILLISECONDS.convert(windowSize, windowUnit);
monitoringWindowStartTime = monitoringWindowEndTime.minus(windowMillis);
}
// write to alert_jobs
Long jobExecutionId = createJob(monitoringWindowStartTime, monitoringWindowEndTime);
alertJobContext.setJobExecutionId(jobExecutionId);
// write to alert_tasks
List<Long> taskIds = createTasks(monitoringWindowStartTime, monitoringWindowEndTime);
}
}
private long createJob(DateTime monitoringWindowStartTime, DateTime monitoringWindowEndTime) {
Long jobExecutionId = null;
try {
JobDTO jobSpec = new JobDTO();
jobSpec.setJobName(alertJobContext.getJobName());
jobSpec.setWindowStartTime(monitoringWindowStartTime.getMillis());
jobSpec.setWindowEndTime(monitoringWindowEndTime.getMillis());
jobSpec.setScheduleStartTime(System.currentTimeMillis());
jobSpec.setStatus(JobStatus.SCHEDULED);
jobSpec.setTaskType(TaskType.ALERT);
jobExecutionId = jobDAO.save(jobSpec);
LOG.info("Created alert job {} with jobExecutionId {}", jobSpec,
jobExecutionId);
} catch (Exception e) {
LOG.error("Exception in creating alert job", e);
}
return jobExecutionId;
}
private List<Long> createTasks(DateTime monitoringWindowStartTime, DateTime monitoringWindowEndTime) {
List<Long> taskIds = new ArrayList<>();
try {
List<AlertTaskInfo> tasks =
taskGenerator.createAlertTasks(alertJobContext, monitoringWindowStartTime, monitoringWindowEndTime);
for (AlertTaskInfo taskInfo : tasks) {
String taskInfoJson = null;
try {
taskInfoJson = OBJECT_MAPPER.writeValueAsString(taskInfo);
} catch (JsonProcessingException e) {
LOG.error("Exception when converting AlertTaskInfo {} to jsonString", taskInfo, e);
}
TaskDTO taskSpec = new TaskDTO();
taskSpec.setTaskType(TaskType.ALERT);
taskSpec.setJobName(alertJobContext.getJobName());
taskSpec.setStatus(TaskStatus.WAITING);
taskSpec.setStartTime(System.currentTimeMillis());
taskSpec.setTaskInfo(taskInfoJson);
taskSpec.setJobId(alertJobContext.getJobExecutionId());
long taskId = taskDAO.save(taskSpec);
taskIds.add(taskId);
LOG.info("Created alert task {} with taskId {}", taskSpec, taskId);
}
} catch (Exception e) {
LOG.error("Exception in creating alert tasks", e);
}
return taskIds;
}
}