diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/tasks/TaskInfo.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/tasks/TaskInfo.java index 7ace6a52df315..a2321064061e1 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/tasks/TaskInfo.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/tasks/TaskInfo.java @@ -29,6 +29,7 @@ public class TaskInfo { private long startTime; private long runningTimeNanos; private boolean cancellable; + private boolean cancelled; private TaskId parentTaskId; private final Map status = new HashMap<>(); private final Map headers = new HashMap<>(); @@ -93,6 +94,14 @@ void setCancellable(boolean cancellable) { this.cancellable = cancellable; } + public boolean isCancelled() { + return cancelled; + } + + void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + public TaskId getParentTaskId() { return parentTaskId; } @@ -134,6 +143,7 @@ private void noOpParse(Object s) {} parser.declareLong(TaskInfo::setStartTime, new ParseField("start_time_in_millis")); parser.declareLong(TaskInfo::setRunningTimeNanos, new ParseField("running_time_in_nanos")); parser.declareBoolean(TaskInfo::setCancellable, new ParseField("cancellable")); + parser.declareBoolean(TaskInfo::setCancelled, new ParseField("cancelled")); parser.declareString(TaskInfo::setParentTaskId, new ParseField("parent_task_id")); parser.declareObject(TaskInfo::setHeaders, (p, c) -> p.mapStrings(), new ParseField("headers")); PARSER = (XContentParser p, Void v, String name) -> parser.parse(p, new TaskInfo(new TaskId(name)), null); @@ -147,6 +157,7 @@ public boolean equals(Object o) { return getStartTime() == taskInfo.getStartTime() && getRunningTimeNanos() == taskInfo.getRunningTimeNanos() && isCancellable() == taskInfo.isCancellable() && + isCancelled() == taskInfo.isCancelled() && Objects.equals(getTaskId(), taskInfo.getTaskId()) && Objects.equals(getType(), taskInfo.getType()) && Objects.equals(getAction(), taskInfo.getAction()) && @@ -159,8 +170,17 @@ public boolean equals(Object o) { @Override public int hashCode() { return Objects.hash( - getTaskId(), getType(), getAction(), getDescription(), getStartTime(), - getRunningTimeNanos(), isCancellable(), getParentTaskId(), status, getHeaders() + getTaskId(), + getType(), + getAction(), + getDescription(), + getStartTime(), + getRunningTimeNanos(), + isCancellable(), + isCancelled(), + getParentTaskId(), + status, + getHeaders() ); } @@ -175,6 +195,7 @@ public String toString() { ", startTime=" + startTime + ", runningTimeNanos=" + runningTimeNanos + ", cancellable=" + cancellable + + ", cancelled=" + cancelled + ", parentTaskId=" + parentTaskId + ", status=" + status + ", headers=" + headers + diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/core/tasks/GetTaskResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/core/tasks/GetTaskResponseTests.java index 53acbbbdce17f..202ad9e6bbe68 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/core/tasks/GetTaskResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/core/tasks/GetTaskResponseTests.java @@ -69,11 +69,23 @@ static TaskInfo randomTaskInfo() { long startTime = randomLong(); long runningTimeNanos = randomLong(); boolean cancellable = randomBoolean(); + boolean cancelled = cancellable && randomBoolean(); TaskId parentTaskId = randomBoolean() ? TaskId.EMPTY_TASK_ID : randomTaskId(); Map headers = randomBoolean() ? Collections.emptyMap() : Collections.singletonMap(randomAlphaOfLength(5), randomAlphaOfLength(5)); - return new TaskInfo(taskId, type, action, description, status, startTime, runningTimeNanos, cancellable, parentTaskId, headers); + return new TaskInfo( + taskId, + type, + action, + description, + status, + startTime, + runningTimeNanos, + cancellable, + cancelled, + parentTaskId, + headers); } private static TaskId randomTaskId() { diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/enrich/StatsResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/enrich/StatsResponseTests.java index a3b3d9e608884..7efbca89294fb 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/enrich/StatsResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/enrich/StatsResponseTests.java @@ -78,10 +78,22 @@ private static TaskInfo randomTaskInfo() { long startTime = randomLong(); long runningTimeNanos = randomNonNegativeLong(); boolean cancellable = randomBoolean(); + boolean cancelled = cancellable && randomBoolean(); TaskId parentTaskId = TaskId.EMPTY_TASK_ID; Map headers = randomBoolean() ? Collections.emptyMap() : Collections.singletonMap(randomAlphaOfLength(5), randomAlphaOfLength(5)); - return new TaskInfo(taskId, type, action, description, null, startTime, runningTimeNanos, cancellable, parentTaskId, headers); + return new TaskInfo( + taskId, + type, + action, + description, + null, + startTime, + runningTimeNanos, + cancellable, + cancelled, + parentTaskId, + headers); } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/tasks/CancelTasksResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/tasks/CancelTasksResponseTests.java index 6ff2b2d7e26fe..54cc6080727a5 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/tasks/CancelTasksResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/tasks/CancelTasksResponseTests.java @@ -58,6 +58,7 @@ protected CancelTasksResponseTests.ByNodeCancelTasksResponse createServerTestIns } for (int i = 0; i < 4; i++) { + boolean isCancellable = randomBoolean(); tasks.add(new org.elasticsearch.tasks.TaskInfo( new TaskId(NODE_ID, (long) i), randomAlphaOfLength(4), @@ -66,7 +67,8 @@ protected CancelTasksResponseTests.ByNodeCancelTasksResponse createServerTestIns new FakeTaskStatus(randomAlphaOfLength(4), randomInt()), randomLongBetween(1, 3), randomIntBetween(5, 10), - false, + isCancellable, + isCancellable && randomBoolean(), new TaskId("node1", randomLong()), Collections.singletonMap("x-header-of", "some-value"))); } @@ -100,6 +102,7 @@ protected void assertInstances(ByNodeCancelTasksResponse serverTestInstance, assertEquals(ti.getStartTime(), taskInfo.getStartTime()); assertEquals(ti.getRunningTimeNanos(), taskInfo.getRunningTimeNanos()); assertEquals(ti.isCancellable(), taskInfo.isCancellable()); + assertEquals(ti.isCancelled(), taskInfo.isCancelled()); assertEquals(ti.getParentTaskId().getNodeId(), taskInfo.getParentTaskId().getNodeId()); assertEquals(ti.getParentTaskId().getId(), taskInfo.getParentTaskId().getId()); FakeTaskStatus status = (FakeTaskStatus) ti.getStatus(); diff --git a/docs/reference/cluster/tasks.asciidoc b/docs/reference/cluster/tasks.asciidoc index 94fa143f6258a..13f966aa58cb0 100644 --- a/docs/reference/cluster/tasks.asciidoc +++ b/docs/reference/cluster/tasks.asciidoc @@ -171,7 +171,8 @@ The API returns the following result: "description" : "indices[test], types[test], search_type[QUERY_THEN_FETCH], source[{\"query\":...}]", "start_time_in_millis" : 1483478610008, "running_time_in_nanos" : 13991383, - "cancellable" : true + "cancellable" : true, + "cancelled" : false } } } @@ -243,6 +244,13 @@ nodes `nodeId1` and `nodeId2`. POST _tasks/_cancel?nodes=nodeId1,nodeId2&actions=*reindex -------------------------------------------------- +A task may continue to run for some time after it has been cancelled because it +may not be able to safely stop its current activity straight away. The list +tasks API will continue to list these cancelled tasks until they complete. The +`cancelled` flag in the response to the list tasks API indicates that the +cancellation command has been processed and the task will stop as soon as +possible. + ===== Task Grouping The task lists returned by task API commands can be grouped either by nodes diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/TransportRethrottleActionTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/TransportRethrottleActionTests.java index 828316a10a291..d24b7220d79d7 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/TransportRethrottleActionTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/TransportRethrottleActionTests.java @@ -91,8 +91,18 @@ public void testRethrottleSuccessfulResponse() { List sliceStatuses = new ArrayList<>(slices); for (int i = 0; i < slices; i++) { BulkByScrollTask.Status status = believeableInProgressStatus(i); - tasks.add(new TaskInfo(new TaskId("test", 123), "test", "test", "test", status, 0, 0, true, new TaskId("test", task.getId()), - Collections.emptyMap())); + tasks.add(new TaskInfo( + new TaskId("test", 123), + "test", + "test", + "test", + status, + 0, + 0, + true, + false, + new TaskId("test", task.getId()), + Collections.emptyMap())); sliceStatuses.add(new BulkByScrollTask.StatusOrException(status)); } rethrottleTestCase(slices, @@ -112,8 +122,18 @@ public void testRethrottleWithSomeSucceeded() { List tasks = new ArrayList<>(); for (int i = succeeded; i < slices; i++) { BulkByScrollTask.Status status = believeableInProgressStatus(i); - tasks.add(new TaskInfo(new TaskId("test", 123), "test", "test", "test", status, 0, 0, true, new TaskId("test", task.getId()), - Collections.emptyMap())); + tasks.add(new TaskInfo( + new TaskId("test", 123), + "test", + "test", + "test", + status, + 0, + 0, + true, + false, + new TaskId("test", task.getId()), + Collections.emptyMap())); sliceStatuses.add(new BulkByScrollTask.StatusOrException(status)); } rethrottleTestCase(slices - succeeded, diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java index c443a2fcabbf1..331ba376463b6 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java @@ -818,8 +818,19 @@ public void testNodeNotFoundButTaskFound() throws Exception { CyclicBarrier b = new CyclicBarrier(2); TaskResultsService resultsService = internalCluster().getInstance(TaskResultsService.class); resultsService.storeResult(new TaskResult( - new TaskInfo(new TaskId("fake", 1), "test", "test", "", null, 0, 0, false, TaskId.EMPTY_TASK_ID, Collections.emptyMap()), - new RuntimeException("test")), + new TaskInfo( + new TaskId("fake", 1), + "test", + "test", + "", + null, + 0, + 0, + false, + false, + TaskId.EMPTY_TASK_ID, + Collections.emptyMap()), + new RuntimeException("test")), new ActionListener() { @Override public void onResponse(Void response) { diff --git a/server/src/main/java/org/elasticsearch/tasks/Task.java b/server/src/main/java/org/elasticsearch/tasks/Task.java index bd296a3ed02c7..9b0a77f5be9d3 100644 --- a/server/src/main/java/org/elasticsearch/tasks/Task.java +++ b/server/src/main/java/org/elasticsearch/tasks/Task.java @@ -90,8 +90,18 @@ public final TaskInfo taskInfo(String localNodeId, boolean detailed) { * Build a proper {@link TaskInfo} for this task. */ protected final TaskInfo taskInfo(String localNodeId, String description, Status status) { - return new TaskInfo(new TaskId(localNodeId, getId()), getType(), getAction(), description, status, startTime, - System.nanoTime() - startTimeNanos, this instanceof CancellableTask, parentTask, headers); + return new TaskInfo( + new TaskId(localNodeId, getId()), + getType(), + getAction(), + description, + status, + startTime, + System.nanoTime() - startTimeNanos, + this instanceof CancellableTask, + this instanceof CancellableTask && ((CancellableTask)this).isCancelled(), + parentTask, + headers); } /** diff --git a/server/src/main/java/org/elasticsearch/tasks/TaskInfo.java b/server/src/main/java/org/elasticsearch/tasks/TaskInfo.java index c1945048168a2..639fbac03b41d 100644 --- a/server/src/main/java/org/elasticsearch/tasks/TaskInfo.java +++ b/server/src/main/java/org/elasticsearch/tasks/TaskInfo.java @@ -41,6 +41,9 @@ * snapshot information about currently running tasks. */ public final class TaskInfo implements Writeable, ToXContentFragment { + + static final String INCLUDE_CANCELLED_PARAM = "include_cancelled"; + private final TaskId taskId; private final String type; @@ -57,12 +60,25 @@ public final class TaskInfo implements Writeable, ToXContentFragment { private final boolean cancellable; + private final boolean cancelled; + private final TaskId parentTaskId; private final Map headers; - public TaskInfo(TaskId taskId, String type, String action, String description, Task.Status status, long startTime, - long runningTimeNanos, boolean cancellable, TaskId parentTaskId, Map headers) { + public TaskInfo( + TaskId taskId, + String type, + String action, + String description, + Task.Status status, + long startTime, + long runningTimeNanos, + boolean cancellable, + boolean cancelled, + TaskId parentTaskId, + Map headers) { + assert cancellable || cancelled == false : "uncancellable task cannot be cancelled"; this.taskId = taskId; this.type = type; this.action = action; @@ -71,6 +87,7 @@ public TaskInfo(TaskId taskId, String type, String action, String description, T this.startTime = startTime; this.runningTimeNanos = runningTimeNanos; this.cancellable = cancellable; + this.cancelled = cancelled; this.parentTaskId = parentTaskId; this.headers = headers; } @@ -87,6 +104,12 @@ public TaskInfo(StreamInput in) throws IOException { startTime = in.readLong(); runningTimeNanos = in.readLong(); cancellable = in.readBoolean(); + if (in.getVersion().onOrAfter(Version.V_7_14_0)) { + cancelled = in.readBoolean(); + } else { + cancelled = false; // older versions do not report when tasks are cancelled so we just assume they aren't + } + assert cancellable || cancelled == false : "uncancellable task cannot be cancelled"; parentTaskId = TaskId.readFromStream(in); if (in.getVersion().onOrAfter(Version.V_6_2_0)) { headers = in.readMap(StreamInput::readString, StreamInput::readString); @@ -105,6 +128,9 @@ public void writeTo(StreamOutput out) throws IOException { out.writeLong(startTime); out.writeLong(runningTimeNanos); out.writeBoolean(cancellable); + if (out.getVersion().onOrAfter(Version.V_7_14_0)) { + out.writeBoolean(cancelled); + } // older versions do not report when tasks are cancelled anyway so it's ok just to drop this flag parentTaskId.writeTo(out); if (out.getVersion().onOrAfter(Version.V_6_2_0)) { out.writeMap(headers, StreamOutput::writeString, StreamOutput::writeString); @@ -160,6 +186,13 @@ public boolean isCancellable() { return cancellable; } + /** + * Returns true if the task supports cancellation and has been cancelled + */ + public boolean isCancelled() { + return cancelled; + } + /** * Returns the parent task id */ @@ -192,6 +225,12 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } builder.field("running_time_in_nanos", runningTimeNanos); builder.field("cancellable", cancellable); + + if (params.paramAsBoolean(INCLUDE_CANCELLED_PARAM, true) && cancellable) { + // don't record this on entries in the tasks index, since we can't add this field to the mapping dynamically and it's not + // important for completed tasks anyway + builder.field("cancelled", cancelled); + } if (parentTaskId.isSet()) { builder.field("parent_task_id", parentTaskId.toString()); } @@ -218,6 +257,7 @@ public static TaskInfo fromXContent(XContentParser parser) { long startTime = (Long) a[i++]; long runningTimeNanos = (Long) a[i++]; boolean cancellable = (Boolean) a[i++]; + boolean cancelled = a[i++] == Boolean.TRUE; String parentTaskIdString = (String) a[i++]; @SuppressWarnings("unchecked") Map headers = (Map) a[i++]; if (headers == null) { @@ -226,8 +266,18 @@ public static TaskInfo fromXContent(XContentParser parser) { } RawTaskStatus status = statusBytes == null ? null : new RawTaskStatus(statusBytes); TaskId parentTaskId = parentTaskIdString == null ? TaskId.EMPTY_TASK_ID : new TaskId(parentTaskIdString); - return new TaskInfo(id, type, action, description, status, startTime, runningTimeNanos, cancellable, parentTaskId, - headers); + return new TaskInfo( + id, + type, + action, + description, + status, + startTime, + runningTimeNanos, + cancellable, + cancelled, + parentTaskId, + headers); }); static { // Note for the future: this has to be backwards and forwards compatible with all changes to the task storage format @@ -241,6 +291,7 @@ public static TaskInfo fromXContent(XContentParser parser) { PARSER.declareLong(constructorArg(), new ParseField("start_time_in_millis")); PARSER.declareLong(constructorArg(), new ParseField("running_time_in_nanos")); PARSER.declareBoolean(constructorArg(), new ParseField("cancellable")); + PARSER.declareBoolean(optionalConstructorArg(), new ParseField("cancelled")); PARSER.declareString(optionalConstructorArg(), new ParseField("parent_task_id")); PARSER.declareObject(optionalConstructorArg(), (p, c) -> p.mapStrings(), new ParseField("headers")); } @@ -265,12 +316,24 @@ public boolean equals(Object obj) { && Objects.equals(runningTimeNanos, other.runningTimeNanos) && Objects.equals(parentTaskId, other.parentTaskId) && Objects.equals(cancellable, other.cancellable) + && Objects.equals(cancelled, other.cancelled) && Objects.equals(status, other.status) && Objects.equals(headers, other.headers); } @Override public int hashCode() { - return Objects.hash(taskId, type, action, description, startTime, runningTimeNanos, parentTaskId, cancellable, status, headers); + return Objects.hash( + taskId, + type, + action, + description, + startTime, + runningTimeNanos, + parentTaskId, + cancellable, + cancelled, + status, + headers); } } diff --git a/server/src/main/java/org/elasticsearch/tasks/TaskResultsService.java b/server/src/main/java/org/elasticsearch/tasks/TaskResultsService.java index d427271873737..8af124a203f8d 100644 --- a/server/src/main/java/org/elasticsearch/tasks/TaskResultsService.java +++ b/server/src/main/java/org/elasticsearch/tasks/TaskResultsService.java @@ -32,11 +32,13 @@ import java.io.IOException; import java.io.UncheckedIOException; +import java.util.Collections; import java.util.Iterator; import static org.elasticsearch.action.admin.cluster.node.tasks.get.GetTaskAction.TASKS_ORIGIN; import static org.elasticsearch.common.unit.TimeValue.timeValueMillis; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.elasticsearch.tasks.TaskInfo.INCLUDE_CANCELLED_PARAM; /** * Service that can store task results. @@ -82,7 +84,7 @@ public TaskResultsService(Client client, ThreadPool threadPool) { public void storeResult(TaskResult taskResult, ActionListener listener) { IndexRequestBuilder index = client.prepareIndex(TASK_INDEX, TASK_TYPE).setId(taskResult.getTask().getTaskId().toString()); try (XContentBuilder builder = XContentFactory.contentBuilder(Requests.INDEX_CONTENT_TYPE)) { - taskResult.toXContent(builder, ToXContent.EMPTY_PARAMS); + taskResult.toXContent(builder, new ToXContent.MapParams(Collections.singletonMap(INCLUDE_CANCELLED_PARAM, "false"))); index.setSource(builder); } catch (IOException e) { throw new ElasticsearchException("Couldn't convert task result to XContent for [{}]", e, taskResult.getTask()); diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TaskTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TaskTests.java index f746508598104..bb8b16a149430 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TaskTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TaskTests.java @@ -25,9 +25,19 @@ public void testTaskInfoToString() { long startTime = randomNonNegativeLong(); long runningTime = randomNonNegativeLong(); boolean cancellable = randomBoolean(); - TaskInfo taskInfo = new TaskInfo(new TaskId(nodeId, taskId), "test_type", - "test_action", "test_description", null, startTime, runningTime, cancellable, TaskId.EMPTY_TASK_ID, - Collections.singletonMap("foo", "bar")); + boolean cancelled = cancellable && randomBoolean(); + TaskInfo taskInfo = new TaskInfo( + new TaskId(nodeId, taskId), + "test_type", + "test_action", + "test_description", + null, + startTime, + runningTime, + cancellable, + cancelled, + TaskId.EMPTY_TASK_ID, + Collections.singletonMap("foo", "bar")); String taskInfoString = taskInfo.toString(); Map map = XContentHelper.convertToMap(new BytesArray(taskInfoString.getBytes(StandardCharsets.UTF_8)), true).v2(); assertEquals(((Number)map.get("id")).longValue(), taskId); @@ -37,6 +47,11 @@ public void testTaskInfoToString() { assertEquals(((Number)map.get("start_time_in_millis")).longValue(), startTime); assertEquals(((Number)map.get("running_time_in_nanos")).longValue(), runningTime); assertEquals(map.get("cancellable"), cancellable); + if (cancellable) { + assertEquals(map.get("cancelled"), cancelled); + } else { + assertFalse(map.containsKey("cancelled")); + } assertEquals(map.get("headers"), Collections.singletonMap("foo", "bar")); } diff --git a/server/src/test/java/org/elasticsearch/tasks/ListTasksResponseTests.java b/server/src/test/java/org/elasticsearch/tasks/ListTasksResponseTests.java index bdbd2cefd7724..623c041498808 100644 --- a/server/src/test/java/org/elasticsearch/tasks/ListTasksResponseTests.java +++ b/server/src/test/java/org/elasticsearch/tasks/ListTasksResponseTests.java @@ -40,7 +40,7 @@ public void testEmptyToString() { public void testNonEmptyToString() { TaskInfo info = new TaskInfo( - new TaskId("node1", 1), "dummy-type", "dummy-action", "dummy-description", null, 0, 1, true, new TaskId("node1", 0), + new TaskId("node1", 1), "dummy-type", "dummy-action", "dummy-description", null, 0, 1, true, false, new TaskId("node1", 0), Collections.singletonMap("foo", "bar")); ListTasksResponse tasksResponse = new ListTasksResponse(singletonList(info), emptyList(), emptyList()); assertEquals("{\n" + @@ -56,6 +56,7 @@ public void testNonEmptyToString() { " \"running_time\" : \"1nanos\",\n" + " \"running_time_in_nanos\" : 1,\n" + " \"cancellable\" : true,\n" + + " \"cancelled\" : false,\n" + " \"parent_task_id\" : \"node1:0\",\n" + " \"headers\" : {\n" + " \"foo\" : \"bar\"\n" + diff --git a/server/src/test/java/org/elasticsearch/tasks/TaskInfoTests.java b/server/src/test/java/org/elasticsearch/tasks/TaskInfoTests.java index 4dc20abb3ff95..42e2a56366a22 100644 --- a/server/src/test/java/org/elasticsearch/tasks/TaskInfoTests.java +++ b/server/src/test/java/org/elasticsearch/tasks/TaskInfoTests.java @@ -61,40 +61,135 @@ protected TaskInfo mutateInstance(TaskInfo info) { switch (between(0, 9)) { case 0: TaskId taskId = new TaskId(info.getTaskId().getNodeId() + randomAlphaOfLength(5), info.getTaskId().getId()); - return new TaskInfo(taskId, info.getType(), info.getAction(), info.getDescription(), info.getStatus(), - info.getStartTime(), info.getRunningTimeNanos(), info.isCancellable(), info.getParentTaskId(), info.getHeaders()); + return new TaskInfo( + taskId, + info.getType(), + info.getAction(), + info.getDescription(), + info.getStatus(), + info.getStartTime(), + info.getRunningTimeNanos(), + info.isCancellable(), + info.isCancelled(), + info.getParentTaskId(), + info.getHeaders()); case 1: - return new TaskInfo(info.getTaskId(), info.getType() + randomAlphaOfLength(5), info.getAction(), info.getDescription(), - info.getStatus(), info.getStartTime(), info.getRunningTimeNanos(), info.isCancellable(), info.getParentTaskId(), - info.getHeaders()); + return new TaskInfo( + info.getTaskId(), + info.getType() + randomAlphaOfLength(5), + info.getAction(), + info.getDescription(), + info.getStatus(), + info.getStartTime(), + info.getRunningTimeNanos(), + info.isCancellable(), + info.isCancelled(), + info.getParentTaskId(), + info.getHeaders()); case 2: - return new TaskInfo(info.getTaskId(), info.getType(), info.getAction() + randomAlphaOfLength(5), info.getDescription(), - info.getStatus(), info.getStartTime(), info.getRunningTimeNanos(), info.isCancellable(), info.getParentTaskId(), - info.getHeaders()); + return new TaskInfo( + info.getTaskId(), + info.getType(), + info.getAction() + randomAlphaOfLength(5), + info.getDescription(), + info.getStatus(), + info.getStartTime(), + info.getRunningTimeNanos(), + info.isCancellable(), + info.isCancelled(), + info.getParentTaskId(), + info.getHeaders()); case 3: - return new TaskInfo(info.getTaskId(), info.getType(), info.getAction(), info.getDescription() + randomAlphaOfLength(5), - info.getStatus(), info.getStartTime(), info.getRunningTimeNanos(), info.isCancellable(), info.getParentTaskId(), - info.getHeaders()); + return new TaskInfo( + info.getTaskId(), + info.getType(), + info.getAction(), + info.getDescription() + randomAlphaOfLength(5), + info.getStatus(), + info.getStartTime(), + info.getRunningTimeNanos(), + info.isCancellable(), + info.isCancelled(), + info.getParentTaskId(), + info.getHeaders()); case 4: Task.Status newStatus = randomValueOtherThan(info.getStatus(), TaskInfoTests::randomRawTaskStatus); - return new TaskInfo(info.getTaskId(), info.getType(), info.getAction(), info.getDescription(), newStatus, - info.getStartTime(), info.getRunningTimeNanos(), info.isCancellable(), info.getParentTaskId(), info.getHeaders()); + return new TaskInfo( + info.getTaskId(), + info.getType(), + info.getAction(), + info.getDescription(), + newStatus, + info.getStartTime(), + info.getRunningTimeNanos(), + info.isCancellable(), + info.isCancelled(), + info.getParentTaskId(), + info.getHeaders()); case 5: - return new TaskInfo(info.getTaskId(), info.getType(), info.getAction(), info.getDescription(), info.getStatus(), - info.getStartTime() + between(1, 100), info.getRunningTimeNanos(), info.isCancellable(), info.getParentTaskId(), - info.getHeaders()); + return new TaskInfo( + info.getTaskId(), + info.getType(), + info.getAction(), + info.getDescription(), + info.getStatus(), + info.getStartTime() + between(1, 100), + info.getRunningTimeNanos(), + info.isCancellable(), + info.isCancelled(), + info.getParentTaskId(), + info.getHeaders()); case 6: - return new TaskInfo(info.getTaskId(), info.getType(), info.getAction(), info.getDescription(), info.getStatus(), - info.getStartTime(), info.getRunningTimeNanos() + between(1, 100), info.isCancellable(), info.getParentTaskId(), - info.getHeaders()); + return new TaskInfo( + info.getTaskId(), + info.getType(), + info.getAction(), + info.getDescription(), + info.getStatus(), + info.getStartTime(), + info.getRunningTimeNanos() + between(1, 100), + info.isCancellable(), + info.isCancelled(), + info.getParentTaskId(), + info.getHeaders()); case 7: - return new TaskInfo(info.getTaskId(), info.getType(), info.getAction(), info.getDescription(), info.getStatus(), - info.getStartTime(), info.getRunningTimeNanos(), info.isCancellable() == false, info.getParentTaskId(), - info.getHeaders()); + // if not cancellable then mutate cancellable flag but leave cancelled flag unset + // if cancelled then mutate cancelled flag but leave cancellable flag set + // if cancellable but not cancelled then mutate exactly one of the flags + // + // cancellable | cancelled | random | cancellable == cancelled | isNowCancellable | isNowCancelled + // false | false | - | true | true | false + // true | true | - | true | true | false + // true | false | false | false | false | false + // true | false | true | false | true | true + boolean isNowCancellable = info.isCancellable() == info.isCancelled() || randomBoolean(); + boolean isNowCancelled = isNowCancellable != (info.isCancellable() == info.isCancelled()); + return new TaskInfo( + info.getTaskId(), + info.getType(), + info.getAction(), + info.getDescription(), + info.getStatus(), + info.getStartTime(), + info.getRunningTimeNanos(), + isNowCancellable, + isNowCancelled, + info.getParentTaskId(), + info.getHeaders()); case 8: TaskId parentId = new TaskId(info.getParentTaskId().getNodeId() + randomAlphaOfLength(5), info.getParentTaskId().getId()); - return new TaskInfo(info.getTaskId(), info.getType(), info.getAction(), info.getDescription(), info.getStatus(), - info.getStartTime(), info.getRunningTimeNanos(), info.isCancellable(), parentId, info.getHeaders()); + return new TaskInfo( + info.getTaskId(), + info.getType(), + info.getAction(), + info.getDescription(), + info.getStatus(), + info.getStartTime(), + info.getRunningTimeNanos(), + info.isCancellable(), + info.isCancelled(), + parentId, + info.getHeaders()); case 9: Map headers = info.getHeaders(); if (headers == null) { @@ -103,8 +198,18 @@ protected TaskInfo mutateInstance(TaskInfo info) { headers = new HashMap<>(info.getHeaders()); } headers.put(randomAlphaOfLength(15), randomAlphaOfLength(15)); - return new TaskInfo(info.getTaskId(), info.getType(), info.getAction(), info.getDescription(), info.getStatus(), - info.getStartTime(), info.getRunningTimeNanos(), info.isCancellable(), info.getParentTaskId(), headers); + return new TaskInfo( + info.getTaskId(), + info.getType(), + info.getAction(), + info.getDescription(), + info.getStatus(), + info.getStartTime(), + info.getRunningTimeNanos(), + info.isCancellable(), + info.isCancelled(), + info.getParentTaskId(), + headers); default: throw new IllegalStateException(); } @@ -119,11 +224,23 @@ static TaskInfo randomTaskInfo() { long startTime = randomLong(); long runningTimeNanos = randomLong(); boolean cancellable = randomBoolean(); + boolean cancelled = cancellable && randomBoolean(); TaskId parentTaskId = randomBoolean() ? TaskId.EMPTY_TASK_ID : randomTaskId(); Map headers = randomBoolean() ? Collections.emptyMap() : Collections.singletonMap(randomAlphaOfLength(5), randomAlphaOfLength(5)); - return new TaskInfo(taskId, type, action, description, status, startTime, runningTimeNanos, cancellable, parentTaskId, headers); + return new TaskInfo( + taskId, + type, + action, + description, + status, + startTime, + runningTimeNanos, + cancellable, + cancelled, + parentTaskId, + headers); } private static TaskId randomTaskId() { diff --git a/x-pack/plugin/enrich/src/test/java/org/elasticsearch/xpack/enrich/action/EnrichStatsResponseTests.java b/x-pack/plugin/enrich/src/test/java/org/elasticsearch/xpack/enrich/action/EnrichStatsResponseTests.java index f2df5648c9d23..07690ceac9b6f 100644 --- a/x-pack/plugin/enrich/src/test/java/org/elasticsearch/xpack/enrich/action/EnrichStatsResponseTests.java +++ b/x-pack/plugin/enrich/src/test/java/org/elasticsearch/xpack/enrich/action/EnrichStatsResponseTests.java @@ -57,10 +57,23 @@ public static TaskInfo randomTaskInfo() { long startTime = randomLong(); long runningTimeNanos = randomNonNegativeLong(); boolean cancellable = randomBoolean(); + boolean cancelled = cancellable && randomBoolean(); TaskId parentTaskId = TaskId.EMPTY_TASK_ID; Map headers = randomBoolean() ? Collections.emptyMap() : Collections.singletonMap(randomAlphaOfLength(5), randomAlphaOfLength(5)); - return new TaskInfo(taskId, type, action, description, null, startTime, runningTimeNanos, cancellable, parentTaskId, headers); + return new TaskInfo( + taskId, + type, + action, + description, + null, + startTime, + runningTimeNanos, + cancellable, + cancelled, + parentTaskId, + headers + ); } } diff --git a/x-pack/plugin/enrich/src/test/java/org/elasticsearch/xpack/monitoring/collector/enrich/ExecutingPolicyDocTests.java b/x-pack/plugin/enrich/src/test/java/org/elasticsearch/xpack/monitoring/collector/enrich/ExecutingPolicyDocTests.java index 68a1c9273c5ee..8f0886fd3bad3 100644 --- a/x-pack/plugin/enrich/src/test/java/org/elasticsearch/xpack/monitoring/collector/enrich/ExecutingPolicyDocTests.java +++ b/x-pack/plugin/enrich/src/test/java/org/elasticsearch/xpack/monitoring/collector/enrich/ExecutingPolicyDocTests.java @@ -124,6 +124,9 @@ public void testToXContent() throws IOException { + "," + "\"cancellable\":" + executingPolicy.getTaskInfo().isCancellable() + + (executingPolicy.getTaskInfo().isCancellable() + ? ",\"cancelled\":" + executingPolicy.getTaskInfo().isCancellable() + : "") + "," + header.map(entry -> String.format(Locale.ROOT, "\"headers\":{\"%s\":\"%s\"}", entry.getKey(), entry.getValue())) .orElse("\"headers\":{}") diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/MlDailyMaintenanceServiceTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/MlDailyMaintenanceServiceTests.java index ce69b5508c8f4..57b32d8cb8f5f 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/MlDailyMaintenanceServiceTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/MlDailyMaintenanceServiceTests.java @@ -163,6 +163,7 @@ public void testJobInDeletingStateAlreadyHasDeletionTask() throws InterruptedExc 0, 0, true, + false, new TaskId("test", 456), Collections.emptyMap());