use of org.opensearch.ad.model.AnomalyDetectorJob in project anomaly-detection by opensearch-project.
the class EntityColdStarter method getEntityColdStartData.
/**
* Get training data for an entity.
*
* We first note the maximum and minimum timestamp, and sample at most 24 points
* (with 60 points apart between two neighboring samples) between those minimum
* and maximum timestamps. Samples can be missing. We only interpolate points
* between present neighboring samples. We then transform samples and interpolate
* points to shingles. Finally, full shingles will be used for cold start.
*
* @param detectorId detector Id
* @param entity the entity's information
* @param listener listener to return training data
*/
private void getEntityColdStartData(String detectorId, Entity entity, ActionListener<Optional<List<double[][]>>> listener) {
ActionListener<Optional<AnomalyDetector>> getDetectorListener = ActionListener.wrap(detectorOp -> {
if (!detectorOp.isPresent()) {
listener.onFailure(new EndRunException(detectorId, "AnomalyDetector is not available.", false));
return;
}
List<double[][]> coldStartData = new ArrayList<>();
AnomalyDetector detector = detectorOp.get();
ActionListener<Optional<Long>> minTimeListener = ActionListener.wrap(earliest -> {
if (earliest.isPresent()) {
long startTimeMs = earliest.get().longValue();
nodeStateManager.getAnomalyDetectorJob(detectorId, ActionListener.wrap(jobOp -> {
if (!jobOp.isPresent()) {
listener.onFailure(new EndRunException(detectorId, "AnomalyDetector job is not available.", false));
return;
}
AnomalyDetectorJob job = jobOp.get();
// End time uses milliseconds as start time is assumed to be in milliseconds.
// Opensearch uses a set of preconfigured formats to recognize and parse these strings into a long value
// representing milliseconds-since-the-epoch in UTC.
// More on https://tinyurl.com/wub4fk92
// Existing samples either predates or coincide with cold start data. In either case,
// combining them without reordering based on time stamps is not ok. We might introduce
// anomalies in the process.
// An ideal solution would be to record time stamps of data points and combine existing
// samples and cold start samples and do interpolation afterwards. Recording time stamps
// requires changes across the board like bwc in checkpoints. A pragmatic solution is to use
// job enabled time as the end time of cold start period as it is easier to combine
// existing samples with cold start data. We just need to appends existing samples after
// cold start data as existing samples all happen after job enabled time. There might
// be some gaps in between the last cold start sample and the first accumulated sample.
// We will need to accept that precision loss in current solution.
long endTimeMs = job.getEnabledTime().toEpochMilli();
Pair<Integer, Integer> params = selectRangeParam(detector);
int stride = params.getLeft();
int numberOfSamples = params.getRight();
// we start with round 0
getFeatures(listener, 0, coldStartData, detector, entity, stride, numberOfSamples, startTimeMs, endTimeMs);
}, listener::onFailure));
} else {
listener.onResponse(Optional.empty());
}
}, listener::onFailure);
searchFeatureDao.getEntityMinDataTime(detector, entity, new ThreadedActionListener<>(logger, threadPool, AnomalyDetectorPlugin.AD_THREAD_POOL_NAME, minTimeListener, false));
}, listener::onFailure);
nodeStateManager.getAnomalyDetector(detectorId, new ThreadedActionListener<>(logger, threadPool, AnomalyDetectorPlugin.AD_THREAD_POOL_NAME, getDetectorListener, false));
}
use of org.opensearch.ad.model.AnomalyDetectorJob in project anomaly-detection by opensearch-project.
the class ADDataMigrator method backfillRealtimeTask.
/**
* Backfill realtiem task for realtime job.
* @param detectorJobs realtime AD jobs
* @param backfillAllJob backfill task for all realtime job or not
*/
public void backfillRealtimeTask(ConcurrentLinkedQueue<AnomalyDetectorJob> detectorJobs, boolean backfillAllJob) {
AnomalyDetectorJob job = detectorJobs.poll();
if (job == null) {
logger.info("AD data migration done.");
if (backfillAllJob) {
this.dataMigrated.set(true);
}
return;
}
String jobId = job.getName();
AnomalyDetectorFunction createRealtimeTaskFunction = () -> {
GetRequest getRequest = new GetRequest(DETECTION_STATE_INDEX, jobId);
client.get(getRequest, ActionListener.wrap(r -> {
if (r != null && r.isExists()) {
try (XContentParser parser = createXContentParserFromRegistry(xContentRegistry, r.getSourceAsBytesRef())) {
ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser);
DetectorInternalState detectorState = DetectorInternalState.parse(parser);
createRealtimeADTask(job, detectorState.getError(), detectorJobs, backfillAllJob);
} catch (IOException e) {
logger.error("Failed to parse detector internal state " + jobId, e);
createRealtimeADTask(job, null, detectorJobs, backfillAllJob);
}
} else {
createRealtimeADTask(job, null, detectorJobs, backfillAllJob);
}
}, e -> {
logger.error("Failed to query detector internal state " + jobId, e);
createRealtimeADTask(job, null, detectorJobs, backfillAllJob);
}));
};
checkIfRealtimeTaskExistsAndBackfill(job, createRealtimeTaskFunction, detectorJobs, backfillAllJob);
}
use of org.opensearch.ad.model.AnomalyDetectorJob in project anomaly-detection by opensearch-project.
the class ADDataMigrator method checkIfRealtimeTaskExistsAndBackfill.
private void checkIfRealtimeTaskExistsAndBackfill(AnomalyDetectorJob job, AnomalyDetectorFunction createRealtimeTaskFunction, ConcurrentLinkedQueue<AnomalyDetectorJob> detectorJobs, boolean migrateAll) {
String jobId = job.getName();
BoolQueryBuilder query = new BoolQueryBuilder();
query.filter(new TermQueryBuilder(DETECTOR_ID_FIELD, jobId));
if (job.isEnabled()) {
query.filter(new TermQueryBuilder(IS_LATEST_FIELD, true));
}
query.filter(new TermsQueryBuilder(TASK_TYPE_FIELD, taskTypeToString(ADTaskType.REALTIME_TASK_TYPES)));
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(query).size(1);
SearchRequest searchRequest = new SearchRequest(DETECTION_STATE_INDEX).source(searchSourceBuilder);
client.search(searchRequest, ActionListener.wrap(r -> {
if (r != null && r.getHits().getTotalHits().value > 0) {
// Backfill next realtime job
backfillRealtimeTask(detectorJobs, migrateAll);
return;
}
createRealtimeTaskFunction.execute();
}, e -> {
if (e instanceof ResourceNotFoundException) {
createRealtimeTaskFunction.execute();
}
logger.error("Failed to search tasks of detector " + jobId);
}));
}
use of org.opensearch.ad.model.AnomalyDetectorJob in project anomaly-detection by opensearch-project.
the class ADDataMigrator method migrateDetectorInternalStateToRealtimeTask.
/**
* Migrate detector internal state to realtime task.
*/
public void migrateDetectorInternalStateToRealtimeTask() {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(new MatchAllQueryBuilder()).size(MAX_DETECTOR_UPPER_LIMIT);
SearchRequest searchRequest = new SearchRequest(ANOMALY_DETECTOR_JOB_INDEX).source(searchSourceBuilder);
client.search(searchRequest, ActionListener.wrap(r -> {
if (r == null || r.getHits().getTotalHits() == null || r.getHits().getTotalHits().value == 0) {
logger.info("No anomaly detector job found, no need to migrate");
return;
}
ConcurrentLinkedQueue<AnomalyDetectorJob> detectorJobs = new ConcurrentLinkedQueue<>();
Iterator<SearchHit> iterator = r.getHits().iterator();
while (iterator.hasNext()) {
SearchHit searchHit = iterator.next();
try (XContentParser parser = createXContentParserFromRegistry(xContentRegistry, searchHit.getSourceRef())) {
ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser);
AnomalyDetectorJob job = AnomalyDetectorJob.parse(parser);
detectorJobs.add(job);
} catch (IOException e) {
logger.error("Fail to parse AD job " + searchHit.getId(), e);
}
}
logger.info("Total AD jobs to backfill realtime task: {}", detectorJobs.size());
backfillRealtimeTask(detectorJobs, true);
}, e -> {
if (ExceptionUtil.getErrorMessage(e).contains("all shards failed")) {
// This error may happen when AD job index not ready for query as some nodes not in cluster yet.
// Will recreate realtime task when AD job starts.
logger.warn("No available shards of AD job index, reset dataMigrated as false");
this.dataMigrated.set(false);
} else if (!(e instanceof IndexNotFoundException)) {
logger.error("Failed to migrate AD data", e);
}
}));
}
use of org.opensearch.ad.model.AnomalyDetectorJob in project anomaly-detection by opensearch-project.
the class AnomalyDetectorJobRunner method runAnomalyDetectionJob.
private void runAnomalyDetectionJob(AnomalyDetectorJob jobParameter, LockService lockService, LockModel lock, Instant detectionStartTime, Instant executionStartTime, String detectorId, String user, List<String> roles) {
try (InjectSecurity injectSecurity = new InjectSecurity(detectorId, settings, client.threadPool().getThreadContext())) {
// Injecting user role to verify if the user has permissions for our API.
injectSecurity.inject(user, roles);
AnomalyResultRequest request = new AnomalyResultRequest(detectorId, detectionStartTime.toEpochMilli(), executionStartTime.toEpochMilli());
client.execute(AnomalyResultAction.INSTANCE, request, ActionListener.wrap(response -> {
indexAnomalyResult(jobParameter, lockService, lock, detectionStartTime, executionStartTime, response);
}, exception -> {
handleAdException(jobParameter, lockService, lock, detectionStartTime, executionStartTime, exception);
}));
} catch (Exception e) {
indexAnomalyResultException(jobParameter, lockService, lock, detectionStartTime, executionStartTime, e, true);
log.error("Failed to execute AD job " + detectorId, e);
}
}
Aggregations