Skip to content

Commit 65a584b

Browse files
authored
[7.x] Report timing stats as part of the Job stats response (#42709) (#43193)
1 parent d27c0fd commit 65a584b

File tree

29 files changed

+1054
-102
lines changed

29 files changed

+1054
-102
lines changed
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.client.ml.job.process;
20+
21+
import org.elasticsearch.client.ml.job.config.Job;
22+
import org.elasticsearch.common.Nullable;
23+
import org.elasticsearch.common.ParseField;
24+
import org.elasticsearch.common.Strings;
25+
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
26+
import org.elasticsearch.common.xcontent.ToXContent;
27+
import org.elasticsearch.common.xcontent.ToXContentObject;
28+
import org.elasticsearch.common.xcontent.XContentBuilder;
29+
30+
import java.io.IOException;
31+
import java.util.Objects;
32+
33+
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
34+
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
35+
36+
/**
37+
* Stats that give more insight into timing of various operations performed as part of anomaly detection job.
38+
*/
39+
public class TimingStats implements ToXContentObject {
40+
41+
public static final ParseField BUCKET_COUNT = new ParseField("bucket_count");
42+
public static final ParseField MIN_BUCKET_PROCESSING_TIME_MS = new ParseField("minimum_bucket_processing_time_ms");
43+
public static final ParseField MAX_BUCKET_PROCESSING_TIME_MS = new ParseField("maximum_bucket_processing_time_ms");
44+
public static final ParseField AVG_BUCKET_PROCESSING_TIME_MS = new ParseField("average_bucket_processing_time_ms");
45+
46+
public static final ConstructingObjectParser<TimingStats, Void> PARSER =
47+
new ConstructingObjectParser<>(
48+
"timing_stats",
49+
true,
50+
args -> new TimingStats((String) args[0], (long) args[1], (Double) args[2], (Double) args[3], (Double) args[4]));
51+
52+
static {
53+
PARSER.declareString(constructorArg(), Job.ID);
54+
PARSER.declareLong(constructorArg(), BUCKET_COUNT);
55+
PARSER.declareDouble(optionalConstructorArg(), MIN_BUCKET_PROCESSING_TIME_MS);
56+
PARSER.declareDouble(optionalConstructorArg(), MAX_BUCKET_PROCESSING_TIME_MS);
57+
PARSER.declareDouble(optionalConstructorArg(), AVG_BUCKET_PROCESSING_TIME_MS);
58+
}
59+
60+
private final String jobId;
61+
private long bucketCount;
62+
private Double minBucketProcessingTimeMs;
63+
private Double maxBucketProcessingTimeMs;
64+
private Double avgBucketProcessingTimeMs;
65+
66+
public TimingStats(
67+
String jobId,
68+
long bucketCount,
69+
@Nullable Double minBucketProcessingTimeMs,
70+
@Nullable Double maxBucketProcessingTimeMs,
71+
@Nullable Double avgBucketProcessingTimeMs) {
72+
this.jobId = jobId;
73+
this.bucketCount = bucketCount;
74+
this.minBucketProcessingTimeMs = minBucketProcessingTimeMs;
75+
this.maxBucketProcessingTimeMs = maxBucketProcessingTimeMs;
76+
this.avgBucketProcessingTimeMs = avgBucketProcessingTimeMs;
77+
}
78+
79+
public String getJobId() {
80+
return jobId;
81+
}
82+
83+
public long getBucketCount() {
84+
return bucketCount;
85+
}
86+
87+
public Double getMinBucketProcessingTimeMs() {
88+
return minBucketProcessingTimeMs;
89+
}
90+
91+
public Double getMaxBucketProcessingTimeMs() {
92+
return maxBucketProcessingTimeMs;
93+
}
94+
95+
public Double getAvgBucketProcessingTimeMs() {
96+
return avgBucketProcessingTimeMs;
97+
}
98+
99+
@Override
100+
public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
101+
builder.startObject();
102+
builder.field(Job.ID.getPreferredName(), jobId);
103+
builder.field(BUCKET_COUNT.getPreferredName(), bucketCount);
104+
if (minBucketProcessingTimeMs != null) {
105+
builder.field(MIN_BUCKET_PROCESSING_TIME_MS.getPreferredName(), minBucketProcessingTimeMs);
106+
}
107+
if (maxBucketProcessingTimeMs != null) {
108+
builder.field(MAX_BUCKET_PROCESSING_TIME_MS.getPreferredName(), maxBucketProcessingTimeMs);
109+
}
110+
if (avgBucketProcessingTimeMs != null) {
111+
builder.field(AVG_BUCKET_PROCESSING_TIME_MS.getPreferredName(), avgBucketProcessingTimeMs);
112+
}
113+
builder.endObject();
114+
return builder;
115+
}
116+
117+
@Override
118+
public boolean equals(Object o) {
119+
if (o == this) return true;
120+
if (o == null || getClass() != o.getClass()) return false;
121+
TimingStats that = (TimingStats) o;
122+
return Objects.equals(this.jobId, that.jobId)
123+
&& this.bucketCount == that.bucketCount
124+
&& Objects.equals(this.minBucketProcessingTimeMs, that.minBucketProcessingTimeMs)
125+
&& Objects.equals(this.maxBucketProcessingTimeMs, that.maxBucketProcessingTimeMs)
126+
&& Objects.equals(this.avgBucketProcessingTimeMs, that.avgBucketProcessingTimeMs);
127+
}
128+
129+
@Override
130+
public int hashCode() {
131+
return Objects.hash(jobId, bucketCount, minBucketProcessingTimeMs, maxBucketProcessingTimeMs, avgBucketProcessingTimeMs);
132+
}
133+
134+
@Override
135+
public String toString() {
136+
return Strings.toString(this);
137+
}
138+
}

client/rest-high-level/src/main/java/org/elasticsearch/client/ml/job/stats/JobStats.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.elasticsearch.client.ml.job.config.JobState;
2323
import org.elasticsearch.client.ml.job.process.DataCounts;
2424
import org.elasticsearch.client.ml.job.process.ModelSizeStats;
25+
import org.elasticsearch.client.ml.job.process.TimingStats;
2526
import org.elasticsearch.common.Nullable;
2627
import org.elasticsearch.common.ParseField;
2728
import org.elasticsearch.common.unit.TimeValue;
@@ -42,6 +43,7 @@ public class JobStats implements ToXContentObject {
4243

4344
private static final ParseField DATA_COUNTS = new ParseField("data_counts");
4445
private static final ParseField MODEL_SIZE_STATS = new ParseField("model_size_stats");
46+
private static final ParseField TIMING_STATS = new ParseField("timing_stats");
4547
private static final ParseField FORECASTS_STATS = new ParseField("forecasts_stats");
4648
private static final ParseField STATE = new ParseField("state");
4749
private static final ParseField NODE = new ParseField("node");
@@ -58,6 +60,7 @@ public class JobStats implements ToXContentObject {
5860
JobState jobState = (JobState) a[i++];
5961
ModelSizeStats.Builder modelSizeStatsBuilder = (ModelSizeStats.Builder) a[i++];
6062
ModelSizeStats modelSizeStats = modelSizeStatsBuilder == null ? null : modelSizeStatsBuilder.build();
63+
TimingStats timingStats = (TimingStats) a[i++];
6164
ForecastStats forecastStats = (ForecastStats) a[i++];
6265
NodeAttributes node = (NodeAttributes) a[i++];
6366
String assignmentExplanation = (String) a[i++];
@@ -66,6 +69,7 @@ public class JobStats implements ToXContentObject {
6669
dataCounts,
6770
jobState,
6871
modelSizeStats,
72+
timingStats,
6973
forecastStats,
7074
node,
7175
assignmentExplanation,
@@ -80,6 +84,7 @@ public class JobStats implements ToXContentObject {
8084
STATE,
8185
ObjectParser.ValueType.VALUE);
8286
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), ModelSizeStats.PARSER, MODEL_SIZE_STATS);
87+
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), TimingStats.PARSER, TIMING_STATS);
8388
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), ForecastStats.PARSER, FORECASTS_STATS);
8489
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), NodeAttributes.PARSER, NODE);
8590
PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), ASSIGNMENT_EXPLANATION);
@@ -94,22 +99,24 @@ public class JobStats implements ToXContentObject {
9499
private final DataCounts dataCounts;
95100
private final JobState state;
96101
private final ModelSizeStats modelSizeStats;
102+
private final TimingStats timingStats;
97103
private final ForecastStats forecastStats;
98104
private final NodeAttributes node;
99105
private final String assignmentExplanation;
100106
private final TimeValue openTime;
101107

102108
JobStats(String jobId, DataCounts dataCounts, JobState state, @Nullable ModelSizeStats modelSizeStats,
103-
@Nullable ForecastStats forecastStats, @Nullable NodeAttributes node,
104-
@Nullable String assignmentExplanation, @Nullable TimeValue opentime) {
109+
@Nullable TimingStats timingStats, @Nullable ForecastStats forecastStats, @Nullable NodeAttributes node,
110+
@Nullable String assignmentExplanation, @Nullable TimeValue openTime) {
105111
this.jobId = Objects.requireNonNull(jobId);
106112
this.dataCounts = Objects.requireNonNull(dataCounts);
107113
this.state = Objects.requireNonNull(state);
108114
this.modelSizeStats = modelSizeStats;
115+
this.timingStats = timingStats;
109116
this.forecastStats = forecastStats;
110117
this.node = node;
111118
this.assignmentExplanation = assignmentExplanation;
112-
this.openTime = opentime;
119+
this.openTime = openTime;
113120
}
114121

115122
/**
@@ -135,6 +142,10 @@ public ModelSizeStats getModelSizeStats() {
135142
return modelSizeStats;
136143
}
137144

145+
public TimingStats getTimingStats() {
146+
return timingStats;
147+
}
148+
138149
/**
139150
* An object that provides statistical information about forecasts of this job.
140151
* See {@link ForecastStats}
@@ -182,6 +193,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
182193
if (modelSizeStats != null) {
183194
builder.field(MODEL_SIZE_STATS.getPreferredName(), modelSizeStats);
184195
}
196+
if (timingStats != null) {
197+
builder.field(TIMING_STATS.getPreferredName(), timingStats);
198+
}
185199
if (forecastStats != null) {
186200
builder.field(FORECASTS_STATS.getPreferredName(), forecastStats);
187201
}
@@ -199,7 +213,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
199213

200214
@Override
201215
public int hashCode() {
202-
return Objects.hash(jobId, dataCounts, modelSizeStats, forecastStats, state, node, assignmentExplanation, openTime);
216+
return Objects.hash(jobId, dataCounts, modelSizeStats, timingStats, forecastStats, state, node, assignmentExplanation, openTime);
203217
}
204218

205219
@Override
@@ -216,6 +230,7 @@ public boolean equals(Object obj) {
216230
return Objects.equals(jobId, other.jobId) &&
217231
Objects.equals(this.dataCounts, other.dataCounts) &&
218232
Objects.equals(this.modelSizeStats, other.modelSizeStats) &&
233+
Objects.equals(this.timingStats, other.timingStats) &&
219234
Objects.equals(this.forecastStats, other.forecastStats) &&
220235
Objects.equals(this.state, other.state) &&
221236
Objects.equals(this.node, other.node) &&
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.client.ml.job.process;
20+
21+
import org.elasticsearch.common.xcontent.XContentParser;
22+
import org.elasticsearch.test.AbstractXContentTestCase;
23+
24+
import static org.hamcrest.Matchers.equalTo;
25+
26+
public class TimingStatsTests extends AbstractXContentTestCase<TimingStats> {
27+
28+
private static final String JOB_ID = "my-job-id";
29+
30+
public static TimingStats createTestInstance(String jobId) {
31+
return new TimingStats(
32+
jobId,
33+
randomLong(),
34+
randomBoolean() ? null : randomDouble(),
35+
randomBoolean() ? null : randomDouble(),
36+
randomBoolean() ? null : randomDouble());
37+
}
38+
39+
@Override
40+
public TimingStats createTestInstance() {
41+
return createTestInstance(randomAlphaOfLength(10));
42+
}
43+
44+
@Override
45+
protected TimingStats doParseInstance(XContentParser parser) {
46+
return TimingStats.PARSER.apply(parser, null);
47+
}
48+
49+
@Override
50+
protected boolean supportsUnknownFields() {
51+
return true;
52+
}
53+
54+
public void testConstructor() {
55+
TimingStats stats = new TimingStats(JOB_ID, 7, 1.0, 2.0, 1.23);
56+
57+
assertThat(stats.getJobId(), equalTo(JOB_ID));
58+
assertThat(stats.getBucketCount(), equalTo(7L));
59+
assertThat(stats.getMinBucketProcessingTimeMs(), equalTo(1.0));
60+
assertThat(stats.getMaxBucketProcessingTimeMs(), equalTo(2.0));
61+
assertThat(stats.getAvgBucketProcessingTimeMs(), equalTo(1.23));
62+
}
63+
64+
public void testConstructor_NullValues() {
65+
TimingStats stats = new TimingStats(JOB_ID, 7, null, null, null);
66+
67+
assertThat(stats.getJobId(), equalTo(JOB_ID));
68+
assertThat(stats.getBucketCount(), equalTo(7L));
69+
assertNull(stats.getMinBucketProcessingTimeMs());
70+
assertNull(stats.getMaxBucketProcessingTimeMs());
71+
assertNull(stats.getAvgBucketProcessingTimeMs());
72+
}
73+
74+
public void testEquals() {
75+
TimingStats stats1 = new TimingStats(JOB_ID, 7, 1.0, 2.0, 1.23);
76+
TimingStats stats2 = new TimingStats(JOB_ID, 7, 1.0, 2.0, 1.23);
77+
TimingStats stats3 = new TimingStats(JOB_ID, 7, 1.0, 3.0, 1.23);
78+
79+
assertTrue(stats1.equals(stats1));
80+
assertTrue(stats1.equals(stats2));
81+
assertFalse(stats2.equals(stats3));
82+
}
83+
84+
public void testHashCode() {
85+
TimingStats stats1 = new TimingStats(JOB_ID, 7, 1.0, 2.0, 1.23);
86+
TimingStats stats2 = new TimingStats(JOB_ID, 7, 1.0, 2.0, 1.23);
87+
TimingStats stats3 = new TimingStats(JOB_ID, 7, 1.0, 3.0, 1.23);
88+
89+
assertEquals(stats1.hashCode(), stats1.hashCode());
90+
assertEquals(stats1.hashCode(), stats2.hashCode());
91+
assertNotEquals(stats2.hashCode(), stats3.hashCode());
92+
}
93+
}

client/rest-high-level/src/test/java/org/elasticsearch/client/ml/job/stats/JobStatsTests.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import org.elasticsearch.client.ml.job.process.DataCountsTests;
2525
import org.elasticsearch.client.ml.job.process.ModelSizeStats;
2626
import org.elasticsearch.client.ml.job.process.ModelSizeStatsTests;
27+
import org.elasticsearch.client.ml.job.process.TimingStats;
28+
import org.elasticsearch.client.ml.job.process.TimingStatsTests;
2729
import org.elasticsearch.common.unit.TimeValue;
2830
import org.elasticsearch.common.xcontent.XContentParser;
2931
import org.elasticsearch.client.ml.job.config.JobState;
@@ -42,12 +44,14 @@ public static JobStats createRandomInstance() {
4244
DataCounts dataCounts = DataCountsTests.createTestInstance(jobId);
4345

4446
ModelSizeStats modelSizeStats = randomBoolean() ? ModelSizeStatsTests.createRandomized() : null;
47+
TimingStats timingStats = randomBoolean() ? TimingStatsTests.createTestInstance(jobId) : null;
4548
ForecastStats forecastStats = randomBoolean() ? ForecastStatsTests.createRandom(1, 22) : null;
4649
NodeAttributes nodeAttributes = randomBoolean() ? NodeAttributesTests.createRandom() : null;
4750
String assigmentExplanation = randomBoolean() ? randomAlphaOfLength(10) : null;
4851
TimeValue openTime = randomBoolean() ? TimeValue.timeValueMillis(randomIntBetween(1, 10000)) : null;
4952

50-
return new JobStats(jobId, dataCounts, state, modelSizeStats, forecastStats, nodeAttributes, assigmentExplanation, openTime);
53+
return new JobStats(
54+
jobId, dataCounts, state, modelSizeStats, timingStats, forecastStats, nodeAttributes, assigmentExplanation, openTime);
5155
}
5256

5357
@Override

0 commit comments

Comments
 (0)