[ML] Change forecast_id to UUid, add create_time and start_time (elastic/x-pack-elasticsearch#3095)
relates elastic/x-pack-elasticsearch#3093 Original commit: elastic/x-pack-elasticsearch@f586189851
This commit is contained in:
parent
a8b5b138a7
commit
e0affd455d
|
@ -175,13 +175,13 @@ public class ForecastJobAction extends Action<ForecastJobAction.Request, Forecas
|
||||||
public static class Response extends BaseTasksResponse implements Writeable, ToXContentObject {
|
public static class Response extends BaseTasksResponse implements Writeable, ToXContentObject {
|
||||||
|
|
||||||
private boolean acknowledged;
|
private boolean acknowledged;
|
||||||
private long forecastId;
|
private String forecastId;
|
||||||
|
|
||||||
Response() {
|
Response() {
|
||||||
super(null, null);
|
super(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
Response(boolean acknowledged, long forecastId) {
|
Response(boolean acknowledged, String forecastId) {
|
||||||
super(null, null);
|
super(null, null);
|
||||||
this.acknowledged = acknowledged;
|
this.acknowledged = acknowledged;
|
||||||
this.forecastId = forecastId;
|
this.forecastId = forecastId;
|
||||||
|
@ -191,7 +191,7 @@ public class ForecastJobAction extends Action<ForecastJobAction.Request, Forecas
|
||||||
return acknowledged;
|
return acknowledged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getForecastId() {
|
public String getForecastId() {
|
||||||
return forecastId;
|
return forecastId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,14 +199,14 @@ public class ForecastJobAction extends Action<ForecastJobAction.Request, Forecas
|
||||||
public void readFrom(StreamInput in) throws IOException {
|
public void readFrom(StreamInput in) throws IOException {
|
||||||
super.readFrom(in);
|
super.readFrom(in);
|
||||||
acknowledged = in.readBoolean();
|
acknowledged = in.readBoolean();
|
||||||
forecastId = in.readLong();
|
forecastId = in.readString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(StreamOutput out) throws IOException {
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
super.writeTo(out);
|
super.writeTo(out);
|
||||||
out.writeBoolean(acknowledged);
|
out.writeBoolean(acknowledged);
|
||||||
out.writeLong(forecastId);
|
out.writeString(forecastId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -227,7 +227,7 @@ public class ForecastJobAction extends Action<ForecastJobAction.Request, Forecas
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Response other = (Response) obj;
|
Response other = (Response) obj;
|
||||||
return this.acknowledged == other.acknowledged && this.forecastId == other.forecastId;
|
return this.acknowledged == other.acknowledged && Objects.equals(this.forecastId, other.forecastId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -336,12 +336,18 @@ public class ElasticsearchMappings {
|
||||||
.field(TYPE, DOUBLE)
|
.field(TYPE, DOUBLE)
|
||||||
.endObject()
|
.endObject()
|
||||||
.startObject(Forecast.FORECAST_ID.getPreferredName())
|
.startObject(Forecast.FORECAST_ID.getPreferredName())
|
||||||
.field(TYPE, LONG)
|
.field(TYPE, KEYWORD)
|
||||||
.endObject();
|
.endObject();
|
||||||
|
|
||||||
// Forecast Stats Output
|
// Forecast Stats Output
|
||||||
// re-used: TIMESTAMP, PROCESSING_TIME_MS, PROCESSED_RECORD_COUNT, LATEST_RECORD_TIME
|
// re-used: TIMESTAMP, PROCESSING_TIME_MS, PROCESSED_RECORD_COUNT, LATEST_RECORD_TIME
|
||||||
builder.startObject(ForecastRequestStats.END_TIME.getPreferredName())
|
builder.startObject(ForecastRequestStats.START_TIME.getPreferredName())
|
||||||
|
.field(TYPE, DATE)
|
||||||
|
.endObject()
|
||||||
|
.startObject(ForecastRequestStats.END_TIME.getPreferredName())
|
||||||
|
.field(TYPE, DATE)
|
||||||
|
.endObject()
|
||||||
|
.startObject(ForecastRequestStats.CREATE_TIME.getPreferredName())
|
||||||
.field(TYPE, DATE)
|
.field(TYPE, DATE)
|
||||||
.endObject()
|
.endObject()
|
||||||
.startObject(ForecastRequestStats.EXPIRY_TIME.getPreferredName())
|
.startObject(ForecastRequestStats.EXPIRY_TIME.getPreferredName())
|
||||||
|
@ -355,6 +361,9 @@ public class ElasticsearchMappings {
|
||||||
.endObject()
|
.endObject()
|
||||||
.startObject(ForecastRequestStats.STATUS.getPreferredName())
|
.startObject(ForecastRequestStats.STATUS.getPreferredName())
|
||||||
.field(TYPE, KEYWORD)
|
.field(TYPE, KEYWORD)
|
||||||
|
.endObject()
|
||||||
|
.startObject(ForecastRequestStats.MEMORY_USAGE.getPreferredName())
|
||||||
|
.field(TYPE, LONG)
|
||||||
.endObject();
|
.endObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ package org.elasticsearch.xpack.ml.job.process.autodetect.params;
|
||||||
|
|
||||||
import org.elasticsearch.ElasticsearchParseException;
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
import org.elasticsearch.common.ParseField;
|
import org.elasticsearch.common.ParseField;
|
||||||
|
import org.elasticsearch.common.UUIDs;
|
||||||
import org.elasticsearch.common.joda.DateMathParser;
|
import org.elasticsearch.common.joda.DateMathParser;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.index.mapper.DateFieldMapper;
|
import org.elasticsearch.index.mapper.DateFieldMapper;
|
||||||
|
@ -16,18 +17,32 @@ import java.util.Objects;
|
||||||
|
|
||||||
public class ForecastParams {
|
public class ForecastParams {
|
||||||
|
|
||||||
|
private final String forecastId;
|
||||||
|
private final long createTime;
|
||||||
private final long endTime;
|
private final long endTime;
|
||||||
private final long duration;
|
private final long duration;
|
||||||
private final long expiresIn;
|
private final long expiresIn;
|
||||||
private final long forecastId;
|
|
||||||
|
|
||||||
private ForecastParams(long forecastId, long endTime, long duration, long expiresIn) {
|
private ForecastParams(String forecastId, long createTime, long endTime, long duration, long expiresIn) {
|
||||||
this.forecastId = forecastId;
|
this.forecastId = forecastId;
|
||||||
|
this.createTime = createTime;
|
||||||
this.endTime = endTime;
|
this.endTime = endTime;
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
this.expiresIn = expiresIn;
|
this.expiresIn = expiresIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getForecastId() {
|
||||||
|
return forecastId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The forecast create time in seconds from the epoch
|
||||||
|
* @return The create time in seconds from the epoch
|
||||||
|
*/
|
||||||
|
public long getCreateTime() {
|
||||||
|
return createTime;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The forecast end time in seconds from the epoch
|
* The forecast end time in seconds from the epoch
|
||||||
* @return The end time in seconds from the epoch
|
* @return The end time in seconds from the epoch
|
||||||
|
@ -52,18 +67,9 @@ public class ForecastParams {
|
||||||
return expiresIn;
|
return expiresIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The forecast id
|
|
||||||
*
|
|
||||||
* @return The forecast Id
|
|
||||||
*/
|
|
||||||
public long getForecastId() {
|
|
||||||
return forecastId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(forecastId, endTime, duration, expiresIn);
|
return Objects.hash(forecastId, createTime, endTime, duration, expiresIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -75,8 +81,11 @@ public class ForecastParams {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ForecastParams other = (ForecastParams) obj;
|
ForecastParams other = (ForecastParams) obj;
|
||||||
return Objects.equals(forecastId, other.forecastId) && Objects.equals(endTime, other.endTime) &&
|
return Objects.equals(forecastId, other.forecastId)
|
||||||
Objects.equals(duration, other.duration) && Objects.equals(expiresIn, other.expiresIn);
|
&& Objects.equals(createTime, other.createTime)
|
||||||
|
&& Objects.equals(endTime, other.endTime)
|
||||||
|
&& Objects.equals(duration, other.duration)
|
||||||
|
&& Objects.equals(expiresIn, other.expiresIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Builder builder() {
|
public static Builder builder() {
|
||||||
|
@ -84,26 +93,22 @@ public class ForecastParams {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
private final String forecastId;
|
||||||
|
private final long createTimeEpochSecs;
|
||||||
private long endTimeEpochSecs;
|
private long endTimeEpochSecs;
|
||||||
private long durationSecs;
|
private long durationSecs;
|
||||||
private long expiresInSecs;
|
private long expiresInSecs;
|
||||||
private long startTime;
|
|
||||||
private long forecastId;
|
|
||||||
|
|
||||||
private Builder() {
|
private Builder() {
|
||||||
startTime = System.currentTimeMillis();
|
forecastId = UUIDs.base64UUID();
|
||||||
|
createTimeEpochSecs = System.currentTimeMillis() / 1000;
|
||||||
endTimeEpochSecs = 0;
|
endTimeEpochSecs = 0;
|
||||||
forecastId = generateId();
|
|
||||||
durationSecs = 0;
|
durationSecs = 0;
|
||||||
|
|
||||||
// because 0 means never expire, the default is -1
|
// because 0 means never expire, the default is -1
|
||||||
expiresInSecs = -1;
|
expiresInSecs = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long generateId() {
|
|
||||||
return startTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder endTime(String endTime, ParseField paramName) {
|
public Builder endTime(String endTime, ParseField paramName) {
|
||||||
DateMathParser dateMathParser = new DateMathParser(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER);
|
DateMathParser dateMathParser = new DateMathParser(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER);
|
||||||
|
|
||||||
|
@ -132,7 +137,7 @@ public class ForecastParams {
|
||||||
throw new ElasticsearchParseException(Messages.getMessage(Messages.REST_INVALID_DURATION_AND_ENDTIME));
|
throw new ElasticsearchParseException(Messages.getMessage(Messages.REST_INVALID_DURATION_AND_ENDTIME));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ForecastParams(forecastId, endTimeEpochSecs, durationSecs, expiresInSecs);
|
return new ForecastParams(forecastId, createTimeEpochSecs, endTimeEpochSecs, durationSecs, expiresInSecs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,9 +151,9 @@ public class ControlMsgToProcessWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeForecastMessage(ForecastParams params) throws IOException {
|
public void writeForecastMessage(ForecastParams params) throws IOException {
|
||||||
XContentBuilder builder = XContentFactory.jsonBuilder()
|
XContentBuilder builder = XContentFactory.jsonBuilder().startObject();
|
||||||
.startObject()
|
builder.field("forecast_id", params.getForecastId());
|
||||||
.field("forecast_id", params.getForecastId());
|
builder.field("create_time", params.getCreateTime());
|
||||||
|
|
||||||
if (params.getEndTime() != 0) {
|
if (params.getEndTime() != 0) {
|
||||||
builder.field("end_time", params.getEndTime());
|
builder.field("end_time", params.getEndTime());
|
||||||
|
|
|
@ -45,11 +45,11 @@ public class Forecast implements ToXContentObject, Writeable {
|
||||||
|
|
||||||
public static final ConstructingObjectParser<Forecast, Void> PARSER =
|
public static final ConstructingObjectParser<Forecast, Void> PARSER =
|
||||||
new ConstructingObjectParser<>(RESULT_TYPE_VALUE, a ->
|
new ConstructingObjectParser<>(RESULT_TYPE_VALUE, a ->
|
||||||
new Forecast((String) a[0], (long) a[1], (Date) a[2], (long) a[3], (int) a[4]));
|
new Forecast((String) a[0], (String) a[1], (Date) a[2], (long) a[3], (int) a[4]));
|
||||||
|
|
||||||
static {
|
static {
|
||||||
PARSER.declareString(ConstructingObjectParser.constructorArg(), Job.ID);
|
PARSER.declareString(ConstructingObjectParser.constructorArg(), Job.ID);
|
||||||
PARSER.declareLong(ConstructingObjectParser.constructorArg(), FORECAST_ID);
|
PARSER.declareString(ConstructingObjectParser.constructorArg(), FORECAST_ID);
|
||||||
PARSER.declareField(ConstructingObjectParser.constructorArg(), p -> {
|
PARSER.declareField(ConstructingObjectParser.constructorArg(), p -> {
|
||||||
if (p.currentToken() == Token.VALUE_NUMBER) {
|
if (p.currentToken() == Token.VALUE_NUMBER) {
|
||||||
return new Date(p.longValue());
|
return new Date(p.longValue());
|
||||||
|
@ -73,7 +73,7 @@ public class Forecast implements ToXContentObject, Writeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String jobId;
|
private final String jobId;
|
||||||
private final long forecastId;
|
private final String forecastId;
|
||||||
private final Date timestamp;
|
private final Date timestamp;
|
||||||
private final long bucketSpan;
|
private final long bucketSpan;
|
||||||
private int detectorIndex;
|
private int detectorIndex;
|
||||||
|
@ -86,9 +86,9 @@ public class Forecast implements ToXContentObject, Writeable {
|
||||||
private double forecastUpper;
|
private double forecastUpper;
|
||||||
private double forecastPrediction;
|
private double forecastPrediction;
|
||||||
|
|
||||||
public Forecast(String jobId, long forecastId, Date timestamp, long bucketSpan, int detectorIndex) {
|
public Forecast(String jobId, String forecastId, Date timestamp, long bucketSpan, int detectorIndex) {
|
||||||
this.jobId = jobId;
|
this.jobId = Objects.requireNonNull(jobId);
|
||||||
this.forecastId = forecastId;
|
this.forecastId = Objects.requireNonNull(forecastId);
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.bucketSpan = bucketSpan;
|
this.bucketSpan = bucketSpan;
|
||||||
this.detectorIndex = detectorIndex;
|
this.detectorIndex = detectorIndex;
|
||||||
|
@ -96,7 +96,7 @@ public class Forecast implements ToXContentObject, Writeable {
|
||||||
|
|
||||||
public Forecast(StreamInput in) throws IOException {
|
public Forecast(StreamInput in) throws IOException {
|
||||||
jobId = in.readString();
|
jobId = in.readString();
|
||||||
forecastId = in.readLong();
|
forecastId = in.readString();
|
||||||
timestamp = new Date(in.readLong());
|
timestamp = new Date(in.readLong());
|
||||||
partitionFieldName = in.readOptionalString();
|
partitionFieldName = in.readOptionalString();
|
||||||
partitionFieldValue = in.readOptionalString();
|
partitionFieldValue = in.readOptionalString();
|
||||||
|
@ -113,7 +113,7 @@ public class Forecast implements ToXContentObject, Writeable {
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(StreamOutput out) throws IOException {
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
out.writeString(jobId);
|
out.writeString(jobId);
|
||||||
out.writeLong(forecastId);
|
out.writeString(forecastId);
|
||||||
out.writeLong(timestamp.getTime());
|
out.writeLong(timestamp.getTime());
|
||||||
out.writeOptionalString(partitionFieldName);
|
out.writeOptionalString(partitionFieldName);
|
||||||
out.writeOptionalString(partitionFieldValue);
|
out.writeOptionalString(partitionFieldValue);
|
||||||
|
@ -165,7 +165,7 @@ public class Forecast implements ToXContentObject, Writeable {
|
||||||
return jobId;
|
return jobId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getForecastId() {
|
public String getForecastId() {
|
||||||
return forecastId;
|
return forecastId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,7 +264,7 @@ public class Forecast implements ToXContentObject, Writeable {
|
||||||
}
|
}
|
||||||
Forecast that = (Forecast) other;
|
Forecast that = (Forecast) other;
|
||||||
return Objects.equals(this.jobId, that.jobId) &&
|
return Objects.equals(this.jobId, that.jobId) &&
|
||||||
forecastId == that.forecastId &&
|
Objects.equals(this.forecastId, that.forecastId) &&
|
||||||
Objects.equals(this.timestamp, that.timestamp) &&
|
Objects.equals(this.timestamp, that.timestamp) &&
|
||||||
Objects.equals(this.partitionFieldValue, that.partitionFieldValue) &&
|
Objects.equals(this.partitionFieldValue, that.partitionFieldValue) &&
|
||||||
Objects.equals(this.partitionFieldName, that.partitionFieldName) &&
|
Objects.equals(this.partitionFieldName, that.partitionFieldName) &&
|
||||||
|
|
|
@ -10,9 +10,9 @@ import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
import org.elasticsearch.common.io.stream.Writeable;
|
import org.elasticsearch.common.io.stream.Writeable;
|
||||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||||
|
import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
|
||||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
|
|
||||||
import org.elasticsearch.xpack.ml.job.config.Job;
|
import org.elasticsearch.xpack.ml.job.config.Job;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -36,7 +36,9 @@ public class ForecastRequestStats implements ToXContentObject, Writeable {
|
||||||
|
|
||||||
public static final ParseField RESULTS_FIELD = new ParseField(RESULT_TYPE_VALUE);
|
public static final ParseField RESULTS_FIELD = new ParseField(RESULT_TYPE_VALUE);
|
||||||
public static final ParseField FORECAST_ID = new ParseField("forecast_id");
|
public static final ParseField FORECAST_ID = new ParseField("forecast_id");
|
||||||
|
public static final ParseField START_TIME = new ParseField("forecast_start_timestamp");
|
||||||
public static final ParseField END_TIME = new ParseField("forecast_end_timestamp");
|
public static final ParseField END_TIME = new ParseField("forecast_end_timestamp");
|
||||||
|
public static final ParseField CREATE_TIME = new ParseField("forecast_create_timestamp");
|
||||||
public static final ParseField EXPIRY_TIME = new ParseField("forecast_expiry_timestamp");
|
public static final ParseField EXPIRY_TIME = new ParseField("forecast_expiry_timestamp");
|
||||||
public static final ParseField MESSAGES = new ParseField("forecast_messages");
|
public static final ParseField MESSAGES = new ParseField("forecast_messages");
|
||||||
public static final ParseField PROCESSING_TIME_MS = new ParseField("processing_time_ms");
|
public static final ParseField PROCESSING_TIME_MS = new ParseField("processing_time_ms");
|
||||||
|
@ -46,20 +48,24 @@ public class ForecastRequestStats implements ToXContentObject, Writeable {
|
||||||
public static final ParseField MEMORY_USAGE = new ParseField("forecast_memory_bytes");
|
public static final ParseField MEMORY_USAGE = new ParseField("forecast_memory_bytes");
|
||||||
|
|
||||||
public static final ConstructingObjectParser<ForecastRequestStats, Void> PARSER =
|
public static final ConstructingObjectParser<ForecastRequestStats, Void> PARSER =
|
||||||
new ConstructingObjectParser<>(RESULT_TYPE_VALUE, a -> new ForecastRequestStats((String) a[0], (long) a[1]));
|
new ConstructingObjectParser<>(RESULT_TYPE_VALUE, a -> new ForecastRequestStats((String) a[0], (String) a[1]));
|
||||||
|
|
||||||
static {
|
static {
|
||||||
PARSER.declareString(ConstructingObjectParser.constructorArg(), Job.ID);
|
PARSER.declareString(ConstructingObjectParser.constructorArg(), Job.ID);
|
||||||
PARSER.declareLong(ConstructingObjectParser.constructorArg(), FORECAST_ID);
|
PARSER.declareString(ConstructingObjectParser.constructorArg(), FORECAST_ID);
|
||||||
|
|
||||||
PARSER.declareString((modelForecastRequestStats, s) -> {}, Result.RESULT_TYPE);
|
PARSER.declareString((modelForecastRequestStats, s) -> {}, Result.RESULT_TYPE);
|
||||||
PARSER.declareLong(ForecastRequestStats::setRecordCount, PROCESSED_RECORD_COUNT);
|
PARSER.declareLong(ForecastRequestStats::setRecordCount, PROCESSED_RECORD_COUNT);
|
||||||
PARSER.declareStringArray(ForecastRequestStats::setMessages, MESSAGES);
|
PARSER.declareStringArray(ForecastRequestStats::setMessages, MESSAGES);
|
||||||
PARSER.declareField(ForecastRequestStats::setTimeStamp,
|
PARSER.declareField(ForecastRequestStats::setTimeStamp,
|
||||||
p -> Instant.ofEpochMilli(p.longValue()), Result.TIMESTAMP, ValueType.LONG);
|
p -> Instant.ofEpochMilli(p.longValue()), Result.TIMESTAMP, ValueType.LONG);
|
||||||
PARSER.declareField(ForecastRequestStats::setEndTimeStamp,
|
PARSER.declareField(ForecastRequestStats::setStartTime,
|
||||||
|
p -> Instant.ofEpochMilli(p.longValue()), START_TIME, ValueType.LONG);
|
||||||
|
PARSER.declareField(ForecastRequestStats::setEndTime,
|
||||||
p -> Instant.ofEpochMilli(p.longValue()), END_TIME, ValueType.LONG);
|
p -> Instant.ofEpochMilli(p.longValue()), END_TIME, ValueType.LONG);
|
||||||
PARSER.declareField(ForecastRequestStats::setExpiryTimeStamp,
|
PARSER.declareField(ForecastRequestStats::setCreateTime,
|
||||||
|
p -> Instant.ofEpochMilli(p.longValue()), CREATE_TIME, ValueType.LONG);
|
||||||
|
PARSER.declareField(ForecastRequestStats::setExpiryTime,
|
||||||
p -> Instant.ofEpochMilli(p.longValue()), EXPIRY_TIME, ValueType.LONG);
|
p -> Instant.ofEpochMilli(p.longValue()), EXPIRY_TIME, ValueType.LONG);
|
||||||
PARSER.declareDouble(ForecastRequestStats::setProgress, PROGRESS);
|
PARSER.declareDouble(ForecastRequestStats::setProgress, PROGRESS);
|
||||||
PARSER.declareLong(ForecastRequestStats::setProcessingTime, PROCESSING_TIME_MS);
|
PARSER.declareLong(ForecastRequestStats::setProcessingTime, PROCESSING_TIME_MS);
|
||||||
|
@ -90,34 +96,39 @@ public class ForecastRequestStats implements ToXContentObject, Writeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String jobId;
|
private final String jobId;
|
||||||
private final long forecastId;
|
private final String forecastId;
|
||||||
private long recordCount;
|
private long recordCount;
|
||||||
private List<String> messages;
|
private List<String> messages;
|
||||||
private Instant dateStarted = Instant.EPOCH;
|
private Instant timestamp = Instant.EPOCH;
|
||||||
private Instant dateEnded = Instant.EPOCH;
|
private Instant startTime = Instant.EPOCH;
|
||||||
private Instant dateExpires = Instant.EPOCH;
|
private Instant endTime = Instant.EPOCH;
|
||||||
|
private Instant createTime = Instant.EPOCH;
|
||||||
|
private Instant expiryTime = Instant.EPOCH;
|
||||||
private double progress;
|
private double progress;
|
||||||
private long processingTime;
|
private long processingTime;
|
||||||
private long memoryUsage;
|
private long memoryUsage;
|
||||||
private ForecastRequestStatus status = ForecastRequestStatus.OK;
|
private ForecastRequestStatus status = ForecastRequestStatus.OK;
|
||||||
|
|
||||||
public ForecastRequestStats(String jobId, long forecastId) {
|
public ForecastRequestStats(String jobId, String forecastId) {
|
||||||
this.jobId = jobId;
|
this.jobId = Objects.requireNonNull(jobId);
|
||||||
this.forecastId = forecastId;
|
this.forecastId = Objects.requireNonNull(forecastId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ForecastRequestStats(StreamInput in) throws IOException {
|
public ForecastRequestStats(StreamInput in) throws IOException {
|
||||||
jobId = in.readString();
|
jobId = in.readString();
|
||||||
forecastId = in.readLong();
|
forecastId = in.readString();
|
||||||
recordCount = in.readLong();
|
recordCount = in.readLong();
|
||||||
if (in.readBoolean()) {
|
if (in.readBoolean()) {
|
||||||
messages = in.readList(StreamInput::readString);
|
messages = in.readList(StreamInput::readString);
|
||||||
} else {
|
} else {
|
||||||
messages = null;
|
messages = null;
|
||||||
}
|
}
|
||||||
dateStarted = Instant.ofEpochMilli(in.readVLong());
|
|
||||||
dateEnded = Instant.ofEpochMilli(in.readVLong());
|
timestamp = Instant.ofEpochMilli(in.readVLong());
|
||||||
dateExpires = Instant.ofEpochMilli(in.readVLong());
|
startTime = Instant.ofEpochMilli(in.readVLong());
|
||||||
|
endTime = Instant.ofEpochMilli(in.readVLong());
|
||||||
|
createTime = Instant.ofEpochMilli(in.readVLong());
|
||||||
|
expiryTime = Instant.ofEpochMilli(in.readVLong());
|
||||||
progress = in.readDouble();
|
progress = in.readDouble();
|
||||||
processingTime = in.readLong();
|
processingTime = in.readLong();
|
||||||
setMemoryUsage(in.readLong());
|
setMemoryUsage(in.readLong());
|
||||||
|
@ -127,7 +138,7 @@ public class ForecastRequestStats implements ToXContentObject, Writeable {
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(StreamOutput out) throws IOException {
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
out.writeString(jobId);
|
out.writeString(jobId);
|
||||||
out.writeLong(forecastId);
|
out.writeString(forecastId);
|
||||||
out.writeLong(recordCount);
|
out.writeLong(recordCount);
|
||||||
if (messages != null) {
|
if (messages != null) {
|
||||||
out.writeBoolean(true);
|
out.writeBoolean(true);
|
||||||
|
@ -135,9 +146,11 @@ public class ForecastRequestStats implements ToXContentObject, Writeable {
|
||||||
} else {
|
} else {
|
||||||
out.writeBoolean(false);
|
out.writeBoolean(false);
|
||||||
}
|
}
|
||||||
out.writeVLong(dateStarted.toEpochMilli());
|
out.writeVLong(timestamp.toEpochMilli());
|
||||||
out.writeVLong(dateEnded.toEpochMilli());
|
out.writeVLong(startTime.toEpochMilli());
|
||||||
out.writeVLong(dateExpires.toEpochMilli());
|
out.writeVLong(endTime.toEpochMilli());
|
||||||
|
out.writeVLong(createTime.toEpochMilli());
|
||||||
|
out.writeVLong(expiryTime.toEpochMilli());
|
||||||
out.writeDouble(progress);
|
out.writeDouble(progress);
|
||||||
out.writeLong(processingTime);
|
out.writeLong(processingTime);
|
||||||
out.writeLong(getMemoryUsage());
|
out.writeLong(getMemoryUsage());
|
||||||
|
@ -154,14 +167,20 @@ public class ForecastRequestStats implements ToXContentObject, Writeable {
|
||||||
if (messages != null) {
|
if (messages != null) {
|
||||||
builder.field(MESSAGES.getPreferredName(), messages);
|
builder.field(MESSAGES.getPreferredName(), messages);
|
||||||
}
|
}
|
||||||
if (dateStarted.equals(Instant.EPOCH) == false) {
|
if (timestamp.equals(Instant.EPOCH) == false) {
|
||||||
builder.field(Result.TIMESTAMP.getPreferredName(), dateStarted.toEpochMilli());
|
builder.field(Result.TIMESTAMP.getPreferredName(), timestamp.toEpochMilli());
|
||||||
}
|
}
|
||||||
if (dateEnded.equals(Instant.EPOCH) == false) {
|
if (startTime.equals(Instant.EPOCH) == false) {
|
||||||
builder.field(END_TIME.getPreferredName(), dateEnded.toEpochMilli());
|
builder.field(START_TIME.getPreferredName(), startTime.toEpochMilli());
|
||||||
}
|
}
|
||||||
if (dateExpires.equals(Instant.EPOCH) == false) {
|
if (endTime.equals(Instant.EPOCH) == false) {
|
||||||
builder.field(EXPIRY_TIME.getPreferredName(), dateExpires.toEpochMilli());
|
builder.field(END_TIME.getPreferredName(), endTime.toEpochMilli());
|
||||||
|
}
|
||||||
|
if (createTime.equals(Instant.EPOCH) == false) {
|
||||||
|
builder.field(CREATE_TIME.getPreferredName(), createTime.toEpochMilli());
|
||||||
|
}
|
||||||
|
if (expiryTime.equals(Instant.EPOCH) == false) {
|
||||||
|
builder.field(EXPIRY_TIME.getPreferredName(), expiryTime.toEpochMilli());
|
||||||
}
|
}
|
||||||
builder.field(PROGRESS.getPreferredName(), progress);
|
builder.field(PROGRESS.getPreferredName(), progress);
|
||||||
builder.field(PROCESSING_TIME_MS.getPreferredName(), processingTime);
|
builder.field(PROCESSING_TIME_MS.getPreferredName(), processingTime);
|
||||||
|
@ -175,7 +194,7 @@ public class ForecastRequestStats implements ToXContentObject, Writeable {
|
||||||
return jobId;
|
return jobId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getForecastId() {
|
public String getForecastId() {
|
||||||
return forecastId;
|
return forecastId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +212,7 @@ public class ForecastRequestStats implements ToXContentObject, Writeable {
|
||||||
this.recordCount = recordCount;
|
this.recordCount = recordCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getRecordCount() {
|
public long getRecordCount() {
|
||||||
return recordCount;
|
return recordCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,28 +224,44 @@ public class ForecastRequestStats implements ToXContentObject, Writeable {
|
||||||
this.messages = messages;
|
this.messages = messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Instant getDateStarted() {
|
public void setTimeStamp(Instant timestamp) {
|
||||||
return dateStarted;
|
this.timestamp = timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTimeStamp(Instant dateStarted) {
|
public Instant getTimestamp() {
|
||||||
this.dateStarted = dateStarted;
|
return timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Instant getDateEnded() {
|
public void setStartTime(Instant startTime) {
|
||||||
return dateEnded;
|
this.startTime = startTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEndTimeStamp(Instant dateEnded) {
|
public Instant getStartTime() {
|
||||||
this.dateEnded = dateEnded;
|
return startTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setExpiryTimeStamp(Instant dateExpires) {
|
public Instant getEndTime() {
|
||||||
this.dateExpires = dateExpires;
|
return endTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Instant getDateExpired() {
|
public void setEndTime(Instant endTime) {
|
||||||
return dateExpires;
|
this.endTime = endTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreateTime(Instant createTime) {
|
||||||
|
this.createTime = createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instant getCreateTime() {
|
||||||
|
return createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExpiryTime(Instant expiryTime) {
|
||||||
|
this.expiryTime = expiryTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instant getExpiryTime() {
|
||||||
|
return expiryTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -278,12 +313,14 @@ public class ForecastRequestStats implements ToXContentObject, Writeable {
|
||||||
}
|
}
|
||||||
ForecastRequestStats that = (ForecastRequestStats) other;
|
ForecastRequestStats that = (ForecastRequestStats) other;
|
||||||
return Objects.equals(this.jobId, that.jobId) &&
|
return Objects.equals(this.jobId, that.jobId) &&
|
||||||
this.forecastId == that.forecastId &&
|
Objects.equals(this.forecastId, that.forecastId) &&
|
||||||
this.recordCount == that.recordCount &&
|
this.recordCount == that.recordCount &&
|
||||||
Objects.equals(this.messages, that.messages) &&
|
Objects.equals(this.messages, that.messages) &&
|
||||||
Objects.equals(this.dateStarted, that.dateStarted) &&
|
Objects.equals(this.timestamp, that.timestamp) &&
|
||||||
Objects.equals(this.dateEnded, that.dateEnded) &&
|
Objects.equals(this.startTime, that.startTime) &&
|
||||||
Objects.equals(this.dateExpires, that.dateExpires) &&
|
Objects.equals(this.endTime, that.endTime) &&
|
||||||
|
Objects.equals(this.createTime, that.createTime) &&
|
||||||
|
Objects.equals(this.expiryTime, that.expiryTime) &&
|
||||||
this.progress == that.progress &&
|
this.progress == that.progress &&
|
||||||
this.processingTime == that.processingTime &&
|
this.processingTime == that.processingTime &&
|
||||||
this.memoryUsage == that.memoryUsage &&
|
this.memoryUsage == that.memoryUsage &&
|
||||||
|
@ -292,7 +329,7 @@ public class ForecastRequestStats implements ToXContentObject, Writeable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(jobId, forecastId, recordCount, messages, dateStarted, dateEnded, dateExpires,
|
return Objects.hash(jobId, forecastId, recordCount, messages, timestamp, startTime, endTime, createTime, expiryTime,
|
||||||
progress, processingTime, memoryUsage, status);
|
progress, processingTime, memoryUsage, status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,12 +131,15 @@ public final class ReservedFieldNames {
|
||||||
Forecast.FORECAST_PREDICTION.getPreferredName(),
|
Forecast.FORECAST_PREDICTION.getPreferredName(),
|
||||||
Forecast.FORECAST_ID.getPreferredName(),
|
Forecast.FORECAST_ID.getPreferredName(),
|
||||||
|
|
||||||
//re-use: ForecastRequestStats.TIMESTAMP
|
//re-use: TIMESTAMP
|
||||||
|
ForecastRequestStats.START_TIME.getPreferredName(),
|
||||||
ForecastRequestStats.END_TIME.getPreferredName(),
|
ForecastRequestStats.END_TIME.getPreferredName(),
|
||||||
|
ForecastRequestStats.CREATE_TIME.getPreferredName(),
|
||||||
ForecastRequestStats.EXPIRY_TIME.getPreferredName(),
|
ForecastRequestStats.EXPIRY_TIME.getPreferredName(),
|
||||||
ForecastRequestStats.MESSAGES.getPreferredName(),
|
ForecastRequestStats.MESSAGES.getPreferredName(),
|
||||||
ForecastRequestStats.PROGRESS.getPreferredName(),
|
ForecastRequestStats.PROGRESS.getPreferredName(),
|
||||||
ForecastRequestStats.STATUS.getPreferredName(),
|
ForecastRequestStats.STATUS.getPreferredName(),
|
||||||
|
ForecastRequestStats.MEMORY_USAGE.getPreferredName(),
|
||||||
|
|
||||||
ModelSizeStats.MODEL_BYTES_FIELD.getPreferredName(),
|
ModelSizeStats.MODEL_BYTES_FIELD.getPreferredName(),
|
||||||
ModelSizeStats.TOTAL_BY_FIELD_COUNT_FIELD.getPreferredName(),
|
ModelSizeStats.TOTAL_BY_FIELD_COUNT_FIELD.getPreferredName(),
|
||||||
|
|
|
@ -119,7 +119,7 @@ public class ExpiredForecastsRemover implements MlDataRemover {
|
||||||
XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(
|
XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(
|
||||||
NamedXContentRegistry.EMPTY, hit.getSourceRef());
|
NamedXContentRegistry.EMPTY, hit.getSourceRef());
|
||||||
ForecastRequestStats forecastRequestStats = ForecastRequestStats.PARSER.apply(parser, null);
|
ForecastRequestStats forecastRequestStats = ForecastRequestStats.PARSER.apply(parser, null);
|
||||||
if (forecastRequestStats.getDateExpired().toEpochMilli() < cutoffEpochMs) {
|
if (forecastRequestStats.getExpiryTime().toEpochMilli() < cutoffEpochMs) {
|
||||||
forecastsToDelete.add(forecastRequestStats);
|
forecastsToDelete.add(forecastRequestStats);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ public class ForecastJobActionResponseTests extends AbstractStreamableTestCase<R
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Response createTestInstance() {
|
protected Response createTestInstance() {
|
||||||
return new Response(randomBoolean(), randomNonNegativeLong());
|
return new Response(randomBoolean(), randomAlphaOfLength(20));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -90,7 +90,7 @@ public class DeleteExpiredDataIT extends MlNativeAutodetectIntegTestCase {
|
||||||
registerJob(newJobBuilder("snapshots-retention-with-retain").setResultsRetentionDays(null).setModelSnapshotRetentionDays(2L));
|
registerJob(newJobBuilder("snapshots-retention-with-retain").setResultsRetentionDays(null).setModelSnapshotRetentionDays(2L));
|
||||||
registerJob(newJobBuilder("results-and-snapshots-retention").setResultsRetentionDays(1L).setModelSnapshotRetentionDays(2L));
|
registerJob(newJobBuilder("results-and-snapshots-retention").setResultsRetentionDays(1L).setModelSnapshotRetentionDays(2L));
|
||||||
|
|
||||||
List<Long> shortExpiryForecastIds = new ArrayList<>();
|
List<String> shortExpiryForecastIds = new ArrayList<>();
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
long oneDayAgo = now - TimeValue.timeValueHours(48).getMillis() - 1;
|
long oneDayAgo = now - TimeValue.timeValueHours(48).getMillis() - 1;
|
||||||
|
@ -123,16 +123,13 @@ public class DeleteExpiredDataIT extends MlNativeAutodetectIntegTestCase {
|
||||||
|
|
||||||
// Now let's create some forecasts
|
// Now let's create some forecasts
|
||||||
openJob(job.getId());
|
openJob(job.getId());
|
||||||
long forecastShortExpiryId = forecast(job.getId(), TimeValue.timeValueHours(3), TimeValue.timeValueSeconds(1));
|
|
||||||
// We need to wait so that forecasts get different IDs
|
// We must set a very small value for expires_in to keep this testable as the deletion cutoff point is the moment
|
||||||
awaitBusy(() -> false, 5, TimeUnit.MILLISECONDS);
|
// the DeleteExpiredDataAction is called.
|
||||||
|
String forecastShortExpiryId = forecast(job.getId(), TimeValue.timeValueHours(3), TimeValue.timeValueSeconds(1));
|
||||||
shortExpiryForecastIds.add(forecastShortExpiryId);
|
shortExpiryForecastIds.add(forecastShortExpiryId);
|
||||||
|
String forecastDefaultExpiryId = forecast(job.getId(), TimeValue.timeValueHours(3), null);
|
||||||
long forecastDefaultExpiryId = forecast(job.getId(), TimeValue.timeValueHours(3), null);
|
String forecastNoExpiryId = forecast(job.getId(), TimeValue.timeValueHours(3), TimeValue.ZERO);
|
||||||
awaitBusy(() -> false, 5, TimeUnit.MILLISECONDS);
|
|
||||||
|
|
||||||
long forecastNoExpiryId = forecast(job.getId(), TimeValue.timeValueHours(3), TimeValue.ZERO);
|
|
||||||
|
|
||||||
waitForecastToFinish(job.getId(), forecastShortExpiryId);
|
waitForecastToFinish(job.getId(), forecastShortExpiryId);
|
||||||
waitForecastToFinish(job.getId(), forecastDefaultExpiryId);
|
waitForecastToFinish(job.getId(), forecastDefaultExpiryId);
|
||||||
waitForecastToFinish(job.getId(), forecastNoExpiryId);
|
waitForecastToFinish(job.getId(), forecastNoExpiryId);
|
||||||
|
@ -166,8 +163,7 @@ public class DeleteExpiredDataIT extends MlNativeAutodetectIntegTestCase {
|
||||||
List<ForecastRequestStats> forecastStats = getForecastStats();
|
List<ForecastRequestStats> forecastStats = getForecastStats();
|
||||||
assertThat(forecastStats.size(), equalTo(getJobs().size() * 3));
|
assertThat(forecastStats.size(), equalTo(getJobs().size() * 3));
|
||||||
for (ForecastRequestStats forecastStat : forecastStats) {
|
for (ForecastRequestStats forecastStat : forecastStats) {
|
||||||
assertThat(countForecastDocs(forecastStat.getJobId(), forecastStat.getForecastId()),
|
assertThat(countForecastDocs(forecastStat.getJobId(), forecastStat.getForecastId()), equalTo(forecastStat.getRecordCount()));
|
||||||
equalTo((long) forecastStat.getRecordCount()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client().execute(DeleteExpiredDataAction.INSTANCE, new DeleteExpiredDataAction.Request()).get();
|
client().execute(DeleteExpiredDataAction.INSTANCE, new DeleteExpiredDataAction.Request()).get();
|
||||||
|
@ -213,11 +209,10 @@ public class DeleteExpiredDataIT extends MlNativeAutodetectIntegTestCase {
|
||||||
forecastStats = getForecastStats();
|
forecastStats = getForecastStats();
|
||||||
assertThat(forecastStats.size(), equalTo(getJobs().size() * 2));
|
assertThat(forecastStats.size(), equalTo(getJobs().size() * 2));
|
||||||
for (ForecastRequestStats forecastStat : forecastStats) {
|
for (ForecastRequestStats forecastStat : forecastStats) {
|
||||||
assertThat(countForecastDocs(forecastStat.getJobId(), forecastStat.getForecastId()),
|
assertThat(countForecastDocs(forecastStat.getJobId(), forecastStat.getForecastId()), equalTo(forecastStat.getRecordCount()));
|
||||||
equalTo((long) forecastStat.getRecordCount()));
|
|
||||||
}
|
}
|
||||||
for (Job.Builder job : getJobs()) {
|
for (Job.Builder job : getJobs()) {
|
||||||
for (long forecastId : shortExpiryForecastIds) {
|
for (String forecastId : shortExpiryForecastIds) {
|
||||||
assertThat(countForecastDocs(job.getId(), forecastId), equalTo(0L));
|
assertThat(countForecastDocs(job.getId(), forecastId), equalTo(0L));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
package org.elasticsearch.xpack.ml.integration;
|
||||||
|
|
||||||
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
import org.elasticsearch.xpack.ml.job.config.AnalysisConfig;
|
||||||
|
import org.elasticsearch.xpack.ml.job.config.DataDescription;
|
||||||
|
import org.elasticsearch.xpack.ml.job.config.Detector;
|
||||||
|
import org.elasticsearch.xpack.ml.job.config.Job;
|
||||||
|
import org.elasticsearch.xpack.ml.job.results.Bucket;
|
||||||
|
import org.elasticsearch.xpack.ml.job.results.Forecast;
|
||||||
|
import org.elasticsearch.xpack.ml.job.results.ForecastRequestStats;
|
||||||
|
import org.junit.After;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.closeTo;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
|
||||||
|
public class ForecastIT extends MlNativeAutodetectIntegTestCase {
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDownData() throws Exception {
|
||||||
|
cleanUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSingleSeries() throws Exception {
|
||||||
|
Detector.Builder detector = new Detector.Builder("mean", "value");
|
||||||
|
|
||||||
|
TimeValue bucketSpan = TimeValue.timeValueHours(1);
|
||||||
|
AnalysisConfig.Builder analysisConfig = new AnalysisConfig.Builder(Collections.singletonList(detector.build()));
|
||||||
|
analysisConfig.setBucketSpan(bucketSpan);
|
||||||
|
DataDescription.Builder dataDescription = new DataDescription.Builder();
|
||||||
|
dataDescription.setTimeFormat("epoch");
|
||||||
|
Job.Builder job = new Job.Builder("forecast-it-test-single-series");
|
||||||
|
job.setAnalysisConfig(analysisConfig);
|
||||||
|
job.setDataDescription(dataDescription);
|
||||||
|
|
||||||
|
registerJob(job);
|
||||||
|
putJob(job);
|
||||||
|
openJob(job.getId());
|
||||||
|
|
||||||
|
long now = Instant.now().getEpochSecond();
|
||||||
|
long timestamp = now - 50 * bucketSpan.seconds();
|
||||||
|
List<String> data = new ArrayList<>();
|
||||||
|
while (timestamp < now) {
|
||||||
|
data.add(createJsonRecord(createRecord(timestamp, 10.0)));
|
||||||
|
data.add(createJsonRecord(createRecord(timestamp, 30.0)));
|
||||||
|
timestamp += bucketSpan.seconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
postData(job.getId(), data.stream().collect(Collectors.joining()));
|
||||||
|
flushJob(job.getId(), false);
|
||||||
|
|
||||||
|
// Now we can start doing forecast requests
|
||||||
|
|
||||||
|
String forecastIdDefaultDurationDefaultExpiry = forecast(job.getId(), null, null);
|
||||||
|
String forecastIdDuration1HourNoExpiry = forecast(job.getId(), TimeValue.timeValueHours(1), TimeValue.ZERO);
|
||||||
|
String forecastIdDuration3HoursExpiresIn24Hours = forecast(job.getId(), TimeValue.timeValueHours(3), TimeValue.timeValueHours(24));
|
||||||
|
|
||||||
|
waitForecastToFinish(job.getId(), forecastIdDefaultDurationDefaultExpiry);
|
||||||
|
waitForecastToFinish(job.getId(), forecastIdDuration1HourNoExpiry);
|
||||||
|
waitForecastToFinish(job.getId(), forecastIdDuration3HoursExpiresIn24Hours);
|
||||||
|
closeJob(job.getId());
|
||||||
|
|
||||||
|
List<Bucket> buckets = getBuckets(job.getId());
|
||||||
|
Bucket lastBucket = buckets.get(buckets.size() - 1);
|
||||||
|
long lastBucketTime = lastBucket.getTimestamp().getTime();
|
||||||
|
|
||||||
|
// Now let's verify forecasts
|
||||||
|
double expectedForecastValue = 20.0;
|
||||||
|
|
||||||
|
List<ForecastRequestStats> forecastStats = getForecastStats();
|
||||||
|
assertThat(forecastStats.size(), equalTo(3));
|
||||||
|
Map<String, ForecastRequestStats> idToForecastStats = new HashMap<>();
|
||||||
|
forecastStats.stream().forEach(f -> idToForecastStats.put(f.getForecastId(), f));
|
||||||
|
|
||||||
|
{
|
||||||
|
ForecastRequestStats forecastDefaultDurationDefaultExpiry = idToForecastStats.get(forecastIdDefaultDurationDefaultExpiry);
|
||||||
|
assertThat(forecastDefaultDurationDefaultExpiry.getExpiryTime().toEpochMilli(),
|
||||||
|
equalTo(forecastDefaultDurationDefaultExpiry.getCreateTime().toEpochMilli()
|
||||||
|
+ TimeValue.timeValueHours(14 * 24).getMillis()));
|
||||||
|
List<Forecast> forecasts = getForecasts(job.getId(), forecastDefaultDurationDefaultExpiry);
|
||||||
|
assertThat(forecastDefaultDurationDefaultExpiry.getRecordCount(), equalTo(24L));
|
||||||
|
assertThat(forecasts.size(), equalTo(24));
|
||||||
|
assertThat(forecasts.get(0).getTimestamp().getTime(), equalTo(lastBucketTime));
|
||||||
|
for (int i = 0; i < forecasts.size(); i++) {
|
||||||
|
Forecast forecast = forecasts.get(i);
|
||||||
|
assertThat(forecast.getTimestamp().getTime(), equalTo(lastBucketTime + i * bucketSpan.getMillis()));
|
||||||
|
assertThat(forecast.getBucketSpan(), equalTo(bucketSpan.getSeconds()));
|
||||||
|
assertThat(forecast.getForecastPrediction(), closeTo(expectedForecastValue, 0.01));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ForecastRequestStats forecastDuration1HourNoExpiry = idToForecastStats.get(forecastIdDuration1HourNoExpiry);
|
||||||
|
assertThat(forecastDuration1HourNoExpiry.getExpiryTime(), equalTo(Instant.EPOCH));
|
||||||
|
List<Forecast> forecasts = getForecasts(job.getId(), forecastDuration1HourNoExpiry);
|
||||||
|
assertThat(forecastDuration1HourNoExpiry.getRecordCount(), equalTo(1L));
|
||||||
|
assertThat(forecasts.size(), equalTo(1));
|
||||||
|
assertThat(forecasts.get(0).getTimestamp().getTime(), equalTo(lastBucketTime));
|
||||||
|
for (int i = 0; i < forecasts.size(); i++) {
|
||||||
|
Forecast forecast = forecasts.get(i);
|
||||||
|
assertThat(forecast.getTimestamp().getTime(), equalTo(lastBucketTime + i * bucketSpan.getMillis()));
|
||||||
|
assertThat(forecast.getBucketSpan(), equalTo(bucketSpan.getSeconds()));
|
||||||
|
assertThat(forecast.getForecastPrediction(), closeTo(expectedForecastValue, 0.01));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ForecastRequestStats forecastDuration3HoursExpiresIn24Hours = idToForecastStats.get(forecastIdDuration3HoursExpiresIn24Hours);
|
||||||
|
assertThat(forecastDuration3HoursExpiresIn24Hours.getExpiryTime().toEpochMilli(),
|
||||||
|
equalTo(forecastDuration3HoursExpiresIn24Hours.getCreateTime().toEpochMilli()
|
||||||
|
+ TimeValue.timeValueHours(24).getMillis()));
|
||||||
|
List<Forecast> forecasts = getForecasts(job.getId(), forecastDuration3HoursExpiresIn24Hours);
|
||||||
|
assertThat(forecastDuration3HoursExpiresIn24Hours.getRecordCount(), equalTo(3L));
|
||||||
|
assertThat(forecasts.size(), equalTo(3));
|
||||||
|
assertThat(forecasts.get(0).getTimestamp().getTime(), equalTo(lastBucketTime));
|
||||||
|
for (int i = 0; i < forecasts.size(); i++) {
|
||||||
|
Forecast forecast = forecasts.get(i);
|
||||||
|
assertThat(forecast.getTimestamp().getTime(), equalTo(lastBucketTime + i * bucketSpan.getMillis()));
|
||||||
|
assertThat(forecast.getBucketSpan(), equalTo(bucketSpan.getSeconds()));
|
||||||
|
assertThat(forecast.getForecastPrediction(), closeTo(expectedForecastValue, 0.01));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, Object> createRecord(long timestamp, double value) {
|
||||||
|
Map<String, Object> record = new HashMap<>();
|
||||||
|
record.put("time", timestamp);
|
||||||
|
record.put("value", value);
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,8 @@ import org.elasticsearch.index.query.QueryBuilders;
|
||||||
import org.elasticsearch.search.SearchHit;
|
import org.elasticsearch.search.SearchHit;
|
||||||
import org.elasticsearch.search.SearchHits;
|
import org.elasticsearch.search.SearchHits;
|
||||||
import org.elasticsearch.search.SearchModule;
|
import org.elasticsearch.search.SearchModule;
|
||||||
|
import org.elasticsearch.search.sort.SortBuilders;
|
||||||
|
import org.elasticsearch.search.sort.SortOrder;
|
||||||
import org.elasticsearch.tasks.Task;
|
import org.elasticsearch.tasks.Task;
|
||||||
import org.elasticsearch.test.SecurityIntegTestCase;
|
import org.elasticsearch.test.SecurityIntegTestCase;
|
||||||
import org.elasticsearch.test.SecuritySettingsSource;
|
import org.elasticsearch.test.SecuritySettingsSource;
|
||||||
|
@ -291,16 +293,18 @@ abstract class MlNativeAutodetectIntegTestCase extends SecurityIntegTestCase {
|
||||||
return client().execute(PostDataAction.INSTANCE, request).actionGet().getDataCounts();
|
return client().execute(PostDataAction.INSTANCE, request).actionGet().getDataCounts();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected long forecast(String jobId, TimeValue duration, TimeValue expiresIn) {
|
protected String forecast(String jobId, TimeValue duration, TimeValue expiresIn) {
|
||||||
ForecastJobAction.Request request = new ForecastJobAction.Request(jobId);
|
ForecastJobAction.Request request = new ForecastJobAction.Request(jobId);
|
||||||
request.setDuration(duration.getStringRep());
|
if (duration != null) {
|
||||||
|
request.setDuration(duration.getStringRep());
|
||||||
|
}
|
||||||
if (expiresIn != null) {
|
if (expiresIn != null) {
|
||||||
request.setExpiresIn(expiresIn.getStringRep());
|
request.setExpiresIn(expiresIn.getStringRep());
|
||||||
}
|
}
|
||||||
return client().execute(ForecastJobAction.INSTANCE, request).actionGet().getForecastId();
|
return client().execute(ForecastJobAction.INSTANCE, request).actionGet().getForecastId();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void waitForecastToFinish(String jobId, long forecastId) throws Exception {
|
protected void waitForecastToFinish(String jobId, String forecastId) throws Exception {
|
||||||
assertBusy(() -> {
|
assertBusy(() -> {
|
||||||
ForecastRequestStats forecastRequestStats = getForecastStats(jobId, forecastId);
|
ForecastRequestStats forecastRequestStats = getForecastStats(jobId, forecastId);
|
||||||
assertThat(forecastRequestStats, is(notNullValue()));
|
assertThat(forecastRequestStats, is(notNullValue()));
|
||||||
|
@ -308,7 +312,7 @@ abstract class MlNativeAutodetectIntegTestCase extends SecurityIntegTestCase {
|
||||||
}, 30, TimeUnit.SECONDS);
|
}, 30, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ForecastRequestStats getForecastStats(String jobId, long forecastId) {
|
protected ForecastRequestStats getForecastStats(String jobId, String forecastId) {
|
||||||
SearchResponse searchResponse = client().prepareSearch(AnomalyDetectorsIndex.jobResultsAliasedName(jobId))
|
SearchResponse searchResponse = client().prepareSearch(AnomalyDetectorsIndex.jobResultsAliasedName(jobId))
|
||||||
.setQuery(QueryBuilders.boolQuery()
|
.setQuery(QueryBuilders.boolQuery()
|
||||||
.filter(QueryBuilders.termQuery(Result.RESULT_TYPE.getPreferredName(), ForecastRequestStats.RESULT_TYPE_VALUE))
|
.filter(QueryBuilders.termQuery(Result.RESULT_TYPE.getPreferredName(), ForecastRequestStats.RESULT_TYPE_VALUE))
|
||||||
|
@ -350,7 +354,7 @@ abstract class MlNativeAutodetectIntegTestCase extends SecurityIntegTestCase {
|
||||||
return forecastStats;
|
return forecastStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected long countForecastDocs(String jobId, long forecastId) {
|
protected long countForecastDocs(String jobId, String forecastId) {
|
||||||
SearchResponse searchResponse = client().prepareSearch(AnomalyDetectorsIndex.jobResultsIndexPrefix() + "*")
|
SearchResponse searchResponse = client().prepareSearch(AnomalyDetectorsIndex.jobResultsIndexPrefix() + "*")
|
||||||
.setQuery(QueryBuilders.boolQuery()
|
.setQuery(QueryBuilders.boolQuery()
|
||||||
.filter(QueryBuilders.termQuery(Result.RESULT_TYPE.getPreferredName(), Forecast.RESULT_TYPE_VALUE))
|
.filter(QueryBuilders.termQuery(Result.RESULT_TYPE.getPreferredName(), Forecast.RESULT_TYPE_VALUE))
|
||||||
|
@ -360,6 +364,30 @@ abstract class MlNativeAutodetectIntegTestCase extends SecurityIntegTestCase {
|
||||||
return searchResponse.getHits().getTotalHits();
|
return searchResponse.getHits().getTotalHits();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected List<Forecast> getForecasts(String jobId, ForecastRequestStats forecastRequestStats) {
|
||||||
|
List<Forecast> forecasts = new ArrayList<>();
|
||||||
|
|
||||||
|
SearchResponse searchResponse = client().prepareSearch(AnomalyDetectorsIndex.jobResultsIndexPrefix() + "*")
|
||||||
|
.setSize((int) forecastRequestStats.getRecordCount())
|
||||||
|
.setQuery(QueryBuilders.boolQuery()
|
||||||
|
.filter(QueryBuilders.termQuery(Result.RESULT_TYPE.getPreferredName(), Forecast.RESULT_TYPE_VALUE))
|
||||||
|
.filter(QueryBuilders.termQuery(Job.ID.getPreferredName(), jobId))
|
||||||
|
.filter(QueryBuilders.termQuery(Forecast.FORECAST_ID.getPreferredName(), forecastRequestStats.getForecastId())))
|
||||||
|
.addSort(SortBuilders.fieldSort(Result.TIMESTAMP.getPreferredName()).order(SortOrder.ASC))
|
||||||
|
.execute().actionGet();
|
||||||
|
SearchHits hits = searchResponse.getHits();
|
||||||
|
for (SearchHit hit : hits) {
|
||||||
|
try {
|
||||||
|
XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(
|
||||||
|
NamedXContentRegistry.EMPTY, hit.getSourceRef());
|
||||||
|
forecasts.add(Forecast.PARSER.apply(parser, null));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return forecasts;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void ensureClusterStateConsistency() throws IOException {
|
protected void ensureClusterStateConsistency() throws IOException {
|
||||||
if (cluster() != null && cluster().size() > 0) {
|
if (cluster() != null && cluster().size() > 0) {
|
||||||
|
|
|
@ -11,6 +11,10 @@ import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.xpack.ml.job.messages.Messages;
|
import org.elasticsearch.xpack.ml.job.messages.Messages;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||||
import static org.hamcrest.Matchers.lessThanOrEqualTo;
|
import static org.hamcrest.Matchers.lessThanOrEqualTo;
|
||||||
|
|
||||||
|
@ -19,6 +23,14 @@ public class ForecastParamsTests extends ESTestCase {
|
||||||
private static ParseField END = new ParseField("end");
|
private static ParseField END = new ParseField("end");
|
||||||
private static ParseField DURATION = new ParseField("duration");
|
private static ParseField DURATION = new ParseField("duration");
|
||||||
|
|
||||||
|
public void testForecastIdsAreUnique() {
|
||||||
|
Set<String> ids = new HashSet<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ids.add(ForecastParams.builder().build().getForecastId());
|
||||||
|
}
|
||||||
|
assertThat(ids.size(), equalTo(10));
|
||||||
|
}
|
||||||
|
|
||||||
public void test_UnparseableEndTimeThrows() {
|
public void test_UnparseableEndTimeThrows() {
|
||||||
ElasticsearchParseException e =
|
ElasticsearchParseException e =
|
||||||
ESTestCase.expectThrows(ElasticsearchParseException.class, () -> ForecastParams.builder().endTime("bad", END).build());
|
ESTestCase.expectThrows(ElasticsearchParseException.class, () -> ForecastParams.builder().endTime("bad", END).build());
|
||||||
|
|
|
@ -87,12 +87,12 @@ public class AutodetectResultTests extends AbstractSerializingTestCase<Autodetec
|
||||||
modelPlot = null;
|
modelPlot = null;
|
||||||
}
|
}
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
forecast = new Forecast(jobId, randomNonNegativeLong(), new Date(randomLong()), randomNonNegativeLong(), randomInt());
|
forecast = new Forecast(jobId, randomAlphaOfLength(20), new Date(randomLong()), randomNonNegativeLong(), randomInt());
|
||||||
} else {
|
} else {
|
||||||
forecast = null;
|
forecast = null;
|
||||||
}
|
}
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
forecastRequestStats = new ForecastRequestStats(jobId, randomNonNegativeLong());
|
forecastRequestStats = new ForecastRequestStats(jobId, randomAlphaOfLength(20));
|
||||||
} else {
|
} else {
|
||||||
forecastRequestStats = null;
|
forecastRequestStats = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,10 +24,10 @@ public class ForecastRequestStatsTests extends AbstractSerializingTestCase<Forec
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ForecastRequestStats createTestInstance() {
|
protected ForecastRequestStats createTestInstance() {
|
||||||
return createTestInstance("ForecastRequestStatsTest", randomNonNegativeLong());
|
return createTestInstance("ForecastRequestStatsTest", randomAlphaOfLength(20));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ForecastRequestStats createTestInstance(String jobId, long forecastId) {
|
public ForecastRequestStats createTestInstance(String jobId, String forecastId) {
|
||||||
ForecastRequestStats forecastRequestStats = new ForecastRequestStats(jobId, forecastId);
|
ForecastRequestStats forecastRequestStats = new ForecastRequestStats(jobId, forecastId);
|
||||||
|
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
|
@ -45,10 +45,16 @@ public class ForecastRequestStatsTests extends AbstractSerializingTestCase<Forec
|
||||||
forecastRequestStats.setTimeStamp(Instant.ofEpochMilli(randomNonNegativeLong()));
|
forecastRequestStats.setTimeStamp(Instant.ofEpochMilli(randomNonNegativeLong()));
|
||||||
}
|
}
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
forecastRequestStats.setEndTimeStamp(Instant.ofEpochMilli(randomNonNegativeLong()));
|
forecastRequestStats.setStartTime(Instant.ofEpochMilli(randomNonNegativeLong()));
|
||||||
}
|
}
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
forecastRequestStats.setExpiryTimeStamp(Instant.ofEpochMilli(randomNonNegativeLong()));
|
forecastRequestStats.setEndTime(Instant.ofEpochMilli(randomNonNegativeLong()));
|
||||||
|
}
|
||||||
|
if (randomBoolean()) {
|
||||||
|
forecastRequestStats.setCreateTime(Instant.ofEpochMilli(randomNonNegativeLong()));
|
||||||
|
}
|
||||||
|
if (randomBoolean()) {
|
||||||
|
forecastRequestStats.setExpiryTime(Instant.ofEpochMilli(randomNonNegativeLong()));
|
||||||
}
|
}
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
forecastRequestStats.setProgress(randomDouble());
|
forecastRequestStats.setProgress(randomDouble());
|
||||||
|
|
|
@ -27,7 +27,7 @@ public class ForecastTests extends AbstractSerializingTestCase<Forecast> {
|
||||||
|
|
||||||
public Forecast createTestInstance(String jobId) {
|
public Forecast createTestInstance(String jobId) {
|
||||||
Forecast forecast =
|
Forecast forecast =
|
||||||
new Forecast(jobId, randomNonNegativeLong(), new Date(randomLong()),
|
new Forecast(jobId, randomAlphaOfLength(20), new Date(randomLong()),
|
||||||
randomNonNegativeLong(), randomInt());
|
randomNonNegativeLong(), randomInt());
|
||||||
|
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
|
@ -69,7 +69,7 @@ public class ForecastTests extends AbstractSerializingTestCase<Forecast> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testId() {
|
public void testId() {
|
||||||
Forecast forecast = new Forecast("job-foo", 222, new Date(100L), 60L, 2);
|
Forecast forecast = new Forecast("job-foo", "222", new Date(100L), 60L, 2);
|
||||||
String byFieldValue = null;
|
String byFieldValue = null;
|
||||||
String partitionFieldValue = null;
|
String partitionFieldValue = null;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue