Skip to content

Commit 0046d55

Browse files
authored
[ML] Job in index: Restore ability to update cluster state jobs (#35539)
Job updates can apply to cluster state or index jobs this includes reverting a model snapshot and finalizing the job
1 parent 99798d5 commit 0046d55

File tree

6 files changed

+418
-65
lines changed

6 files changed

+418
-65
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/UpdateJobAction.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public static UpdateJobAction.Request parseRequest(String jobId, XContentParser
5454

5555
/** Indicates an update that was not triggered by a user */
5656
private boolean isInternal;
57+
private boolean waitForAck = true;
5758

5859
public Request(String jobId, JobUpdate update) {
5960
this(jobId, update, false);
@@ -87,6 +88,14 @@ public boolean isInternal() {
8788
return isInternal;
8889
}
8990

91+
public boolean isWaitForAck() {
92+
return waitForAck;
93+
}
94+
95+
public void setWaitForAck(boolean waitForAck) {
96+
this.waitForAck = waitForAck;
97+
}
98+
9099
@Override
91100
public ActionRequestValidationException validate() {
92101
return null;
@@ -102,9 +111,10 @@ public void readFrom(StreamInput in) throws IOException {
102111
} else {
103112
isInternal = false;
104113
}
105-
// TODO jindex change CURRENT to specific version when feature branch is merged
106-
if (in.getVersion().onOrAfter(Version.V_6_3_0) && in.getVersion().before(Version.CURRENT)) {
107-
in.readBoolean(); // was waitForAck
114+
if (in.getVersion().onOrAfter(Version.V_6_3_0)) {
115+
waitForAck = in.readBoolean();
116+
} else {
117+
waitForAck = true;
108118
}
109119
}
110120

@@ -116,9 +126,8 @@ public void writeTo(StreamOutput out) throws IOException {
116126
if (out.getVersion().onOrAfter(Version.V_6_2_2)) {
117127
out.writeBoolean(isInternal);
118128
}
119-
// TODO jindex change CURRENT to specific version when feature branch is merged
120-
if (out.getVersion().onOrAfter(Version.V_6_3_0) && out.getVersion().before(Version.CURRENT)) {
121-
out.writeBoolean(false); // was waitForAck
129+
if (out.getVersion().onOrAfter(Version.V_6_3_0)) {
130+
out.writeBoolean(waitForAck);
122131
}
123132
}
124133

x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportFinalizeJobExecutionAction.java

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,27 @@
1010
import org.elasticsearch.action.support.master.AcknowledgedResponse;
1111
import org.elasticsearch.action.support.master.TransportMasterNodeAction;
1212
import org.elasticsearch.cluster.ClusterState;
13+
import org.elasticsearch.cluster.ClusterStateUpdateTask;
1314
import org.elasticsearch.cluster.block.ClusterBlockException;
1415
import org.elasticsearch.cluster.block.ClusterBlockLevel;
1516
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
17+
import org.elasticsearch.cluster.metadata.MetaData;
1618
import org.elasticsearch.cluster.service.ClusterService;
1719
import org.elasticsearch.common.inject.Inject;
1820
import org.elasticsearch.common.settings.Settings;
1921
import org.elasticsearch.threadpool.ThreadPool;
2022
import org.elasticsearch.transport.TransportService;
23+
import org.elasticsearch.xpack.core.XPackPlugin;
24+
import org.elasticsearch.xpack.core.ml.MlMetadata;
2125
import org.elasticsearch.xpack.core.ml.action.FinalizeJobExecutionAction;
26+
import org.elasticsearch.xpack.core.ml.job.config.Job;
2227

28+
import java.util.Arrays;
29+
import java.util.Date;
30+
import java.util.List;
31+
import java.util.stream.Collectors;
32+
33+
// This action is only called from modes before version 6.6.0
2334
public class TransportFinalizeJobExecutionAction extends TransportMasterNodeAction<FinalizeJobExecutionAction.Request,
2435
AcknowledgedResponse> {
2536

@@ -45,9 +56,55 @@ protected AcknowledgedResponse newResponse() {
4556
@Override
4657
protected void masterOperation(FinalizeJobExecutionAction.Request request, ClusterState state,
4758
ActionListener<AcknowledgedResponse> listener) {
48-
// This action is no longer required but needs to be preserved
49-
// in case it is called by an old node in a mixed cluster
50-
listener.onResponse(new AcknowledgedResponse(true));
59+
60+
MlMetadata mlMetadata = MlMetadata.getMlMetadata(state);
61+
List<String> jobsInClusterState = Arrays.stream(request.getJobIds())
62+
.filter(id -> mlMetadata.getJobs().containsKey(id))
63+
.collect(Collectors.toList());
64+
65+
// This action should not be called for jobs that have
66+
// their configuration in index documents
67+
68+
if (jobsInClusterState.isEmpty()) {
69+
// This action is a no-op for jobs not defined in the cluster state.
70+
listener.onResponse(new AcknowledgedResponse(true));
71+
return;
72+
}
73+
74+
String jobIdString = String.join(",", jobsInClusterState);
75+
String source = "finalize_job_execution [" + jobIdString + "]";
76+
logger.debug("finalizing jobs [{}]", jobIdString);
77+
clusterService.submitStateUpdateTask(source, new ClusterStateUpdateTask() {
78+
@Override
79+
public ClusterState execute(ClusterState currentState) {
80+
XPackPlugin.checkReadyForXPackCustomMetadata(currentState);
81+
MlMetadata mlMetadata = MlMetadata.getMlMetadata(currentState);
82+
MlMetadata.Builder mlMetadataBuilder = new MlMetadata.Builder(mlMetadata);
83+
Date finishedTime = new Date();
84+
85+
for (String jobId : jobsInClusterState) {
86+
Job.Builder jobBuilder = new Job.Builder(mlMetadata.getJobs().get(jobId));
87+
jobBuilder.setFinishedTime(finishedTime);
88+
mlMetadataBuilder.putJob(jobBuilder.build(), true);
89+
}
90+
ClusterState.Builder builder = ClusterState.builder(currentState);
91+
return builder.metaData(new MetaData.Builder(currentState.metaData())
92+
.putCustom(MlMetadata.TYPE, mlMetadataBuilder.build()))
93+
.build();
94+
}
95+
96+
@Override
97+
public void onFailure(String source, Exception e) {
98+
listener.onFailure(e);
99+
}
100+
101+
@Override
102+
public void clusterStateProcessed(String source, ClusterState oldState,
103+
ClusterState newState) {
104+
logger.debug("finalized job [{}]", jobIdString);
105+
listener.onResponse(new AcknowledgedResponse(true));
106+
}
107+
});
51108
}
52109

53110
@Override
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
package org.elasticsearch.xpack.ml.job;
7+
8+
import org.elasticsearch.cluster.ClusterState;
9+
import org.elasticsearch.cluster.metadata.MetaData;
10+
import org.elasticsearch.xpack.core.XPackPlugin;
11+
import org.elasticsearch.xpack.core.ml.MlMetadata;
12+
import org.elasticsearch.xpack.core.ml.job.config.Job;
13+
14+
/**
15+
* Helper functions for managing cluster state job configurations
16+
*/
17+
public final class ClusterStateJobUpdate {
18+
19+
private ClusterStateJobUpdate() {
20+
}
21+
22+
public static boolean jobIsInClusterState(ClusterState clusterState, String jobId) {
23+
MlMetadata mlMetadata = MlMetadata.getMlMetadata(clusterState);
24+
return mlMetadata.getJobs().containsKey(jobId);
25+
}
26+
27+
public static boolean jobIsInMlMetadata(MlMetadata mlMetadata, String jobId) {
28+
return mlMetadata.getJobs().containsKey(jobId);
29+
}
30+
31+
public static ClusterState putJobInClusterState(Job job, boolean overwrite, ClusterState currentState) {
32+
MlMetadata.Builder builder = createMlMetadataBuilder(currentState);
33+
builder.putJob(job, overwrite);
34+
return buildNewClusterState(currentState, builder);
35+
}
36+
37+
private static MlMetadata.Builder createMlMetadataBuilder(ClusterState currentState) {
38+
return new MlMetadata.Builder(MlMetadata.getMlMetadata(currentState));
39+
}
40+
41+
private static ClusterState buildNewClusterState(ClusterState currentState, MlMetadata.Builder builder) {
42+
XPackPlugin.checkReadyForXPackCustomMetadata(currentState);
43+
ClusterState.Builder newState = ClusterState.builder(currentState);
44+
newState.metaData(MetaData.builder(currentState.getMetaData()).putCustom(MlMetadata.TYPE, builder.build()).build());
45+
return newState.build();
46+
}
47+
}

0 commit comments

Comments
 (0)