Add resource stats to task framework (#2089)
* Add resource stats to task framework Signed-off-by: sruti1312 <srutiparthiban@gmail.com> * Update thread resource info and add tests Signed-off-by: sruti1312 <srutiparthiban@gmail.com>
This commit is contained in:
parent
5dd75bb0aa
commit
8b997c1d84
|
@ -57,6 +57,7 @@ public class TaskInfo {
|
||||||
private TaskId parentTaskId;
|
private TaskId parentTaskId;
|
||||||
private final Map<String, Object> status = new HashMap<>();
|
private final Map<String, Object> status = new HashMap<>();
|
||||||
private final Map<String, String> headers = new HashMap<>();
|
private final Map<String, String> headers = new HashMap<>();
|
||||||
|
private final Map<String, Object> resourceStats = new HashMap<>();
|
||||||
|
|
||||||
public TaskInfo(TaskId taskId) {
|
public TaskInfo(TaskId taskId) {
|
||||||
this.taskId = taskId;
|
this.taskId = taskId;
|
||||||
|
@ -150,6 +151,14 @@ public class TaskInfo {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setResourceStats(Map<String, Object> resourceStats) {
|
||||||
|
this.resourceStats.putAll(resourceStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getResourceStats() {
|
||||||
|
return resourceStats;
|
||||||
|
}
|
||||||
|
|
||||||
private void noOpParse(Object s) {}
|
private void noOpParse(Object s) {}
|
||||||
|
|
||||||
public static final ObjectParser.NamedObjectParser<TaskInfo, Void> PARSER;
|
public static final ObjectParser.NamedObjectParser<TaskInfo, Void> PARSER;
|
||||||
|
@ -170,6 +179,7 @@ public class TaskInfo {
|
||||||
parser.declareBoolean(TaskInfo::setCancelled, new ParseField("cancelled"));
|
parser.declareBoolean(TaskInfo::setCancelled, new ParseField("cancelled"));
|
||||||
parser.declareString(TaskInfo::setParentTaskId, new ParseField("parent_task_id"));
|
parser.declareString(TaskInfo::setParentTaskId, new ParseField("parent_task_id"));
|
||||||
parser.declareObject(TaskInfo::setHeaders, (p, c) -> p.mapStrings(), new ParseField("headers"));
|
parser.declareObject(TaskInfo::setHeaders, (p, c) -> p.mapStrings(), new ParseField("headers"));
|
||||||
|
parser.declareObject(TaskInfo::setResourceStats, (p, c) -> p.map(), new ParseField("resource_stats"));
|
||||||
PARSER = (XContentParser p, Void v, String name) -> parser.parse(p, new TaskInfo(new TaskId(name)), null);
|
PARSER = (XContentParser p, Void v, String name) -> parser.parse(p, new TaskInfo(new TaskId(name)), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +198,8 @@ public class TaskInfo {
|
||||||
&& Objects.equals(getDescription(), taskInfo.getDescription())
|
&& Objects.equals(getDescription(), taskInfo.getDescription())
|
||||||
&& Objects.equals(getParentTaskId(), taskInfo.getParentTaskId())
|
&& Objects.equals(getParentTaskId(), taskInfo.getParentTaskId())
|
||||||
&& Objects.equals(status, taskInfo.status)
|
&& Objects.equals(status, taskInfo.status)
|
||||||
&& Objects.equals(getHeaders(), taskInfo.getHeaders());
|
&& Objects.equals(getHeaders(), taskInfo.getHeaders())
|
||||||
|
&& Objects.equals(getResourceStats(), taskInfo.getResourceStats());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -204,7 +215,8 @@ public class TaskInfo {
|
||||||
isCancelled(),
|
isCancelled(),
|
||||||
getParentTaskId(),
|
getParentTaskId(),
|
||||||
status,
|
status,
|
||||||
getHeaders()
|
getHeaders(),
|
||||||
|
getResourceStats()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,6 +248,8 @@ public class TaskInfo {
|
||||||
+ status
|
+ status
|
||||||
+ ", headers="
|
+ ", headers="
|
||||||
+ headers
|
+ headers
|
||||||
|
+ ", resource_stats="
|
||||||
|
+ resourceStats
|
||||||
+ '}';
|
+ '}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,8 @@ import org.opensearch.common.bytes.BytesReference;
|
||||||
import org.opensearch.common.xcontent.ToXContent;
|
import org.opensearch.common.xcontent.ToXContent;
|
||||||
import org.opensearch.common.xcontent.XContentBuilder;
|
import org.opensearch.common.xcontent.XContentBuilder;
|
||||||
import org.opensearch.tasks.RawTaskStatus;
|
import org.opensearch.tasks.RawTaskStatus;
|
||||||
|
import org.opensearch.tasks.TaskResourceStats;
|
||||||
|
import org.opensearch.tasks.TaskResourceUsage;
|
||||||
import org.opensearch.tasks.Task;
|
import org.opensearch.tasks.Task;
|
||||||
import org.opensearch.tasks.TaskId;
|
import org.opensearch.tasks.TaskId;
|
||||||
import org.opensearch.tasks.TaskInfo;
|
import org.opensearch.tasks.TaskInfo;
|
||||||
|
@ -45,6 +47,7 @@ import org.opensearch.test.OpenSearchTestCase;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.opensearch.test.AbstractXContentTestCase.xContentTester;
|
import static org.opensearch.test.AbstractXContentTestCase.xContentTester;
|
||||||
|
@ -57,7 +60,7 @@ public class GetTaskResponseTests extends OpenSearchTestCase {
|
||||||
)
|
)
|
||||||
.assertEqualsConsumer(this::assertEqualInstances)
|
.assertEqualsConsumer(this::assertEqualInstances)
|
||||||
.assertToXContentEquivalence(true)
|
.assertToXContentEquivalence(true)
|
||||||
.randomFieldsExcludeFilter(field -> field.endsWith("headers") || field.endsWith("status"))
|
.randomFieldsExcludeFilter(field -> field.endsWith("headers") || field.endsWith("status") || field.contains("resource_stats"))
|
||||||
.test();
|
.test();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +109,8 @@ public class GetTaskResponseTests extends OpenSearchTestCase {
|
||||||
cancellable,
|
cancellable,
|
||||||
cancelled,
|
cancelled,
|
||||||
parentTaskId,
|
parentTaskId,
|
||||||
headers
|
headers,
|
||||||
|
randomResourceStats()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,4 +131,14 @@ public class GetTaskResponseTests extends OpenSearchTestCase {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static TaskResourceStats randomResourceStats() {
|
||||||
|
return randomBoolean() ? null : new TaskResourceStats(new HashMap<String, TaskResourceUsage>() {
|
||||||
|
{
|
||||||
|
for (int i = 0; i < randomInt(5); i++) {
|
||||||
|
put(randomAlphaOfLength(5), new TaskResourceUsage(randomNonNegativeLong(), randomNonNegativeLong()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,8 @@ public class CancelTasksResponseTests extends AbstractResponseTestCase<
|
||||||
cancellable,
|
cancellable,
|
||||||
cancelled,
|
cancelled,
|
||||||
new TaskId("node1", randomLong()),
|
new TaskId("node1", randomLong()),
|
||||||
Collections.singletonMap("x-header-of", "some-value")
|
Collections.singletonMap("x-header-of", "some-value"),
|
||||||
|
null
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,7 +131,8 @@ public class TransportRethrottleActionTests extends OpenSearchTestCase {
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
new TaskId("test", task.getId()),
|
new TaskId("test", task.getId()),
|
||||||
Collections.emptyMap()
|
Collections.emptyMap(),
|
||||||
|
null
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
sliceStatuses.add(new BulkByScrollTask.StatusOrException(status));
|
sliceStatuses.add(new BulkByScrollTask.StatusOrException(status));
|
||||||
|
@ -167,7 +168,8 @@ public class TransportRethrottleActionTests extends OpenSearchTestCase {
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
new TaskId("test", task.getId()),
|
new TaskId("test", task.getId()),
|
||||||
Collections.emptyMap()
|
Collections.emptyMap(),
|
||||||
|
null
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
sliceStatuses.add(new BulkByScrollTask.StatusOrException(status));
|
sliceStatuses.add(new BulkByScrollTask.StatusOrException(status));
|
||||||
|
|
|
@ -907,7 +907,8 @@ public class TasksIT extends OpenSearchIntegTestCase {
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
TaskId.EMPTY_TASK_ID,
|
TaskId.EMPTY_TASK_ID,
|
||||||
Collections.emptyMap()
|
Collections.emptyMap(),
|
||||||
|
null
|
||||||
),
|
),
|
||||||
new RuntimeException("test")
|
new RuntimeException("test")
|
||||||
),
|
),
|
||||||
|
|
|
@ -137,6 +137,7 @@ public class RestTasksAction extends AbstractCatAction {
|
||||||
// Task detailed info
|
// Task detailed info
|
||||||
if (detailed) {
|
if (detailed) {
|
||||||
table.addCell("description", "default:true;alias:desc;desc:task action");
|
table.addCell("description", "default:true;alias:desc;desc:task action");
|
||||||
|
table.addCell("resource_stats", "default:false;desc:resource consumption info of the task");
|
||||||
}
|
}
|
||||||
table.endHeaders();
|
table.endHeaders();
|
||||||
return table;
|
return table;
|
||||||
|
@ -173,6 +174,7 @@ public class RestTasksAction extends AbstractCatAction {
|
||||||
|
|
||||||
if (detailed) {
|
if (detailed) {
|
||||||
table.addCell(taskInfo.getDescription());
|
table.addCell(taskInfo.getDescription());
|
||||||
|
table.addCell(taskInfo.getResourceStats());
|
||||||
}
|
}
|
||||||
table.endRow();
|
table.endRow();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* The OpenSearch Contributors require contributions made to
|
||||||
|
* this file be licensed under the Apache-2.0 license or a
|
||||||
|
* compatible open source license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.opensearch.tasks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Different resource stats are defined.
|
||||||
|
*/
|
||||||
|
public enum ResourceStats {
|
||||||
|
CPU("cpu_time_in_nanos"),
|
||||||
|
MEMORY("memory_in_bytes");
|
||||||
|
|
||||||
|
private final String statsName;
|
||||||
|
|
||||||
|
ResourceStats(String statsName) {
|
||||||
|
this.statsName = statsName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return statsName;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* The OpenSearch Contributors require contributions made to
|
||||||
|
* this file be licensed under the Apache-2.0 license or a
|
||||||
|
* compatible open source license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.opensearch.tasks;
|
||||||
|
|
||||||
|
/** Defines the different types of resource stats. */
|
||||||
|
public enum ResourceStatsType {
|
||||||
|
// resource stats of the worker thread reported directly from runnable.
|
||||||
|
WORKER_STATS("worker_stats", false);
|
||||||
|
|
||||||
|
private final String statsType;
|
||||||
|
private final boolean onlyForAnalysis;
|
||||||
|
|
||||||
|
ResourceStatsType(String statsType, boolean onlyForAnalysis) {
|
||||||
|
this.statsType = statsType;
|
||||||
|
this.onlyForAnalysis = onlyForAnalysis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOnlyForAnalysis() {
|
||||||
|
return onlyForAnalysis;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return statsType;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* The OpenSearch Contributors require contributions made to
|
||||||
|
* this file be licensed under the Apache-2.0 license or a
|
||||||
|
* compatible open source license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.opensearch.tasks;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread resource usage information for particular resource stats type.
|
||||||
|
* <p>
|
||||||
|
* It captures the resource usage information like memory, CPU about a particular execution of thread
|
||||||
|
* for a specific stats type.
|
||||||
|
*/
|
||||||
|
public class ResourceUsageInfo {
|
||||||
|
private static final Logger logger = LogManager.getLogger(ResourceUsageInfo.class);
|
||||||
|
private final EnumMap<ResourceStats, ResourceStatsInfo> statsInfo = new EnumMap<>(ResourceStats.class);
|
||||||
|
|
||||||
|
public ResourceUsageInfo(ResourceUsageMetric... resourceUsageMetrics) {
|
||||||
|
for (ResourceUsageMetric resourceUsageMetric : resourceUsageMetrics) {
|
||||||
|
this.statsInfo.put(resourceUsageMetric.getStats(), new ResourceStatsInfo(resourceUsageMetric.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void recordResourceUsageMetrics(ResourceUsageMetric... resourceUsageMetrics) {
|
||||||
|
for (ResourceUsageMetric resourceUsageMetric : resourceUsageMetrics) {
|
||||||
|
final ResourceStatsInfo resourceStatsInfo = statsInfo.get(resourceUsageMetric.getStats());
|
||||||
|
if (resourceStatsInfo != null) {
|
||||||
|
updateResourceUsageInfo(resourceStatsInfo, resourceUsageMetric);
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"cannot update ["
|
||||||
|
+ resourceUsageMetric.getStats().toString()
|
||||||
|
+ "] entry as its not present current_stats_info:"
|
||||||
|
+ statsInfo
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateResourceUsageInfo(ResourceStatsInfo resourceStatsInfo, ResourceUsageMetric resourceUsageMetric) {
|
||||||
|
long currentEndValue;
|
||||||
|
long newEndValue;
|
||||||
|
do {
|
||||||
|
currentEndValue = resourceStatsInfo.endValue.get();
|
||||||
|
newEndValue = resourceUsageMetric.getValue();
|
||||||
|
if (currentEndValue > newEndValue) {
|
||||||
|
logger.debug(
|
||||||
|
"dropping resource usage update as the new value is lower than current value ["
|
||||||
|
+ "resource_stats=[{}], "
|
||||||
|
+ "current_end_value={}, "
|
||||||
|
+ "new_end_value={}]",
|
||||||
|
resourceUsageMetric.getStats(),
|
||||||
|
currentEndValue,
|
||||||
|
newEndValue
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} while (!resourceStatsInfo.endValue.compareAndSet(currentEndValue, newEndValue));
|
||||||
|
logger.debug(
|
||||||
|
"updated resource usage info [resource_stats=[{}], " + "old_end_value={}, new_end_value={}]",
|
||||||
|
resourceUsageMetric.getStats(),
|
||||||
|
currentEndValue,
|
||||||
|
newEndValue
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<ResourceStats, ResourceStatsInfo> getStatsInfo() {
|
||||||
|
return Collections.unmodifiableMap(statsInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return statsInfo.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines resource stats information.
|
||||||
|
*/
|
||||||
|
static class ResourceStatsInfo {
|
||||||
|
private final long startValue;
|
||||||
|
private final AtomicLong endValue;
|
||||||
|
|
||||||
|
private ResourceStatsInfo(long startValue) {
|
||||||
|
this.startValue = startValue;
|
||||||
|
this.endValue = new AtomicLong(startValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTotalValue() {
|
||||||
|
return endValue.get() - startValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.valueOf(getTotalValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* The OpenSearch Contributors require contributions made to
|
||||||
|
* this file be licensed under the Apache-2.0 license or a
|
||||||
|
* compatible open source license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.opensearch.tasks;
|
||||||
|
|
||||||
|
public class ResourceUsageMetric {
|
||||||
|
private final ResourceStats stats;
|
||||||
|
private final long value;
|
||||||
|
|
||||||
|
public ResourceUsageMetric(ResourceStats stats, long value) {
|
||||||
|
this.stats = stats;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceStats getStats() {
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,8 @@
|
||||||
|
|
||||||
package org.opensearch.tasks;
|
package org.opensearch.tasks;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.opensearch.action.ActionResponse;
|
import org.opensearch.action.ActionResponse;
|
||||||
import org.opensearch.cluster.node.DiscoveryNode;
|
import org.opensearch.cluster.node.DiscoveryNode;
|
||||||
import org.opensearch.common.io.stream.NamedWriteable;
|
import org.opensearch.common.io.stream.NamedWriteable;
|
||||||
|
@ -39,18 +41,27 @@ import org.opensearch.common.xcontent.ToXContent;
|
||||||
import org.opensearch.common.xcontent.ToXContentObject;
|
import org.opensearch.common.xcontent.ToXContentObject;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current task information
|
* Current task information
|
||||||
*/
|
*/
|
||||||
public class Task {
|
public class Task {
|
||||||
|
|
||||||
|
private static final Logger logger = LogManager.getLogger(Task.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The request header to mark tasks with specific ids
|
* The request header to mark tasks with specific ids
|
||||||
*/
|
*/
|
||||||
public static final String X_OPAQUE_ID = "X-Opaque-Id";
|
public static final String X_OPAQUE_ID = "X-Opaque-Id";
|
||||||
|
|
||||||
|
private static final String TOTAL = "total";
|
||||||
|
|
||||||
private final long id;
|
private final long id;
|
||||||
|
|
||||||
private final String type;
|
private final String type;
|
||||||
|
@ -63,6 +74,8 @@ public class Task {
|
||||||
|
|
||||||
private final Map<String, String> headers;
|
private final Map<String, String> headers;
|
||||||
|
|
||||||
|
private final Map<Long, List<ThreadResourceInfo>> resourceStats;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The task's start time as a wall clock time since epoch ({@link System#currentTimeMillis()} style).
|
* The task's start time as a wall clock time since epoch ({@link System#currentTimeMillis()} style).
|
||||||
*/
|
*/
|
||||||
|
@ -74,7 +87,7 @@ public class Task {
|
||||||
private final long startTimeNanos;
|
private final long startTimeNanos;
|
||||||
|
|
||||||
public Task(long id, String type, String action, String description, TaskId parentTask, Map<String, String> headers) {
|
public Task(long id, String type, String action, String description, TaskId parentTask, Map<String, String> headers) {
|
||||||
this(id, type, action, description, parentTask, System.currentTimeMillis(), System.nanoTime(), headers);
|
this(id, type, action, description, parentTask, System.currentTimeMillis(), System.nanoTime(), headers, new ConcurrentHashMap<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task(
|
public Task(
|
||||||
|
@ -85,7 +98,8 @@ public class Task {
|
||||||
TaskId parentTask,
|
TaskId parentTask,
|
||||||
long startTime,
|
long startTime,
|
||||||
long startTimeNanos,
|
long startTimeNanos,
|
||||||
Map<String, String> headers
|
Map<String, String> headers,
|
||||||
|
ConcurrentHashMap<Long, List<ThreadResourceInfo>> resourceStats
|
||||||
) {
|
) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
@ -95,6 +109,7 @@ public class Task {
|
||||||
this.startTime = startTime;
|
this.startTime = startTime;
|
||||||
this.startTimeNanos = startTimeNanos;
|
this.startTimeNanos = startTimeNanos;
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
|
this.resourceStats = resourceStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,19 +123,48 @@ public class Task {
|
||||||
* generate data?
|
* generate data?
|
||||||
*/
|
*/
|
||||||
public final TaskInfo taskInfo(String localNodeId, boolean detailed) {
|
public final TaskInfo taskInfo(String localNodeId, boolean detailed) {
|
||||||
|
return taskInfo(localNodeId, detailed, detailed == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a version of the task status you can throw over the wire and back
|
||||||
|
* with the option to include resource stats or not.
|
||||||
|
* This method is only used during creating TaskResult to avoid storing resource information into the task index.
|
||||||
|
*
|
||||||
|
* @param excludeStats should information exclude resource stats.
|
||||||
|
* By default, detailed flag is used to control including resource information.
|
||||||
|
* But inorder to avoid storing resource stats into task index as strict mapping is enforced and breaks when adding this field.
|
||||||
|
* In the future, task-index-mapping.json can be modified to add resource stats.
|
||||||
|
*/
|
||||||
|
private TaskInfo taskInfo(String localNodeId, boolean detailed, boolean excludeStats) {
|
||||||
String description = null;
|
String description = null;
|
||||||
Task.Status status = null;
|
Task.Status status = null;
|
||||||
|
TaskResourceStats resourceStats = null;
|
||||||
if (detailed) {
|
if (detailed) {
|
||||||
description = getDescription();
|
description = getDescription();
|
||||||
status = getStatus();
|
status = getStatus();
|
||||||
}
|
}
|
||||||
return taskInfo(localNodeId, description, status);
|
if (excludeStats == false) {
|
||||||
|
resourceStats = new TaskResourceStats(new HashMap<>() {
|
||||||
|
{
|
||||||
|
put(TOTAL, getTotalResourceStats());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return taskInfo(localNodeId, description, status, resourceStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a {@link TaskInfo} for this task without resource stats.
|
||||||
|
*/
|
||||||
|
protected final TaskInfo taskInfo(String localNodeId, String description, Status status) {
|
||||||
|
return taskInfo(localNodeId, description, status, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a proper {@link TaskInfo} for this task.
|
* Build a proper {@link TaskInfo} for this task.
|
||||||
*/
|
*/
|
||||||
protected final TaskInfo taskInfo(String localNodeId, String description, Status status) {
|
protected final TaskInfo taskInfo(String localNodeId, String description, Status status, TaskResourceStats resourceStats) {
|
||||||
return new TaskInfo(
|
return new TaskInfo(
|
||||||
new TaskId(localNodeId, getId()),
|
new TaskId(localNodeId, getId()),
|
||||||
getType(),
|
getType(),
|
||||||
|
@ -132,7 +176,8 @@ public class Task {
|
||||||
this instanceof CancellableTask,
|
this instanceof CancellableTask,
|
||||||
this instanceof CancellableTask && ((CancellableTask) this).isCancelled(),
|
this instanceof CancellableTask && ((CancellableTask) this).isCancelled(),
|
||||||
parentTask,
|
parentTask,
|
||||||
headers
|
headers,
|
||||||
|
resourceStats
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,6 +240,102 @@ public class Task {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns thread level resource consumption of the task
|
||||||
|
*/
|
||||||
|
public Map<Long, List<ThreadResourceInfo>> getResourceStats() {
|
||||||
|
return Collections.unmodifiableMap(resourceStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns current total resource usage of the task.
|
||||||
|
* Currently, this method is only called on demand, during get and listing of tasks.
|
||||||
|
* In the future, these values can be cached as an optimization.
|
||||||
|
*/
|
||||||
|
public TaskResourceUsage getTotalResourceStats() {
|
||||||
|
return new TaskResourceUsage(getTotalResourceUtilization(ResourceStats.CPU), getTotalResourceUtilization(ResourceStats.MEMORY));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns total resource consumption for a specific task stat.
|
||||||
|
*/
|
||||||
|
public long getTotalResourceUtilization(ResourceStats stats) {
|
||||||
|
long totalResourceConsumption = 0L;
|
||||||
|
for (List<ThreadResourceInfo> threadResourceInfosList : resourceStats.values()) {
|
||||||
|
for (ThreadResourceInfo threadResourceInfo : threadResourceInfosList) {
|
||||||
|
final ResourceUsageInfo.ResourceStatsInfo statsInfo = threadResourceInfo.getResourceUsageInfo().getStatsInfo().get(stats);
|
||||||
|
if (threadResourceInfo.getStatsType().isOnlyForAnalysis() == false && statsInfo != null) {
|
||||||
|
totalResourceConsumption += statsInfo.getTotalValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totalResourceConsumption;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds thread's starting resource consumption information
|
||||||
|
* @param threadId ID of the thread
|
||||||
|
* @param statsType stats type
|
||||||
|
* @param resourceUsageMetrics resource consumption metrics of the thread
|
||||||
|
* @throws IllegalStateException matching active thread entry was found which is not expected.
|
||||||
|
*/
|
||||||
|
public void startThreadResourceTracking(long threadId, ResourceStatsType statsType, ResourceUsageMetric... resourceUsageMetrics) {
|
||||||
|
final List<ThreadResourceInfo> threadResourceInfoList = resourceStats.computeIfAbsent(threadId, k -> new ArrayList<>());
|
||||||
|
// active thread entry should not be present in the list
|
||||||
|
for (ThreadResourceInfo threadResourceInfo : threadResourceInfoList) {
|
||||||
|
if (threadResourceInfo.getStatsType() == statsType && threadResourceInfo.isActive()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"unexpected active thread resource entry present [" + threadId + "]:[" + threadResourceInfo + "]"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
threadResourceInfoList.add(new ThreadResourceInfo(statsType, resourceUsageMetrics));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to update the resource consumption stats so that the data isn't too stale for long-running task.
|
||||||
|
* If active thread entry is present in the list, the entry is updated. If one is not found, it throws an exception.
|
||||||
|
* @param threadId ID of the thread
|
||||||
|
* @param statsType stats type
|
||||||
|
* @param resourceUsageMetrics resource consumption metrics of the thread
|
||||||
|
* @throws IllegalStateException if no matching active thread entry was found.
|
||||||
|
*/
|
||||||
|
public void updateThreadResourceStats(long threadId, ResourceStatsType statsType, ResourceUsageMetric... resourceUsageMetrics) {
|
||||||
|
final List<ThreadResourceInfo> threadResourceInfoList = resourceStats.get(threadId);
|
||||||
|
if (threadResourceInfoList != null) {
|
||||||
|
for (ThreadResourceInfo threadResourceInfo : threadResourceInfoList) {
|
||||||
|
// the active entry present in the list is updated
|
||||||
|
if (threadResourceInfo.getStatsType() == statsType && threadResourceInfo.isActive()) {
|
||||||
|
threadResourceInfo.recordResourceUsageMetrics(resourceUsageMetrics);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("cannot update if active thread resource entry is not present");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record the thread's final resource consumption values.
|
||||||
|
* If active thread entry is present in the list, the entry is updated. If one is not found, it throws an exception.
|
||||||
|
* @param threadId ID of the thread
|
||||||
|
* @param statsType stats type
|
||||||
|
* @param resourceUsageMetrics resource consumption metrics of the thread
|
||||||
|
* @throws IllegalStateException if no matching active thread entry was found.
|
||||||
|
*/
|
||||||
|
public void stopThreadResourceTracking(long threadId, ResourceStatsType statsType, ResourceUsageMetric... resourceUsageMetrics) {
|
||||||
|
final List<ThreadResourceInfo> threadResourceInfoList = resourceStats.get(threadId);
|
||||||
|
if (threadResourceInfoList != null) {
|
||||||
|
for (ThreadResourceInfo threadResourceInfo : threadResourceInfoList) {
|
||||||
|
if (threadResourceInfo.getStatsType() == statsType && threadResourceInfo.isActive()) {
|
||||||
|
threadResourceInfo.setActive(false);
|
||||||
|
threadResourceInfo.recordResourceUsageMetrics(resourceUsageMetrics);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("cannot update final values if active thread resource entry is not present");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Report of the internal status of a task. These can vary wildly from task
|
* Report of the internal status of a task. These can vary wildly from task
|
||||||
* to task because each task is implemented differently but we should try
|
* to task because each task is implemented differently but we should try
|
||||||
|
@ -217,12 +358,12 @@ public class Task {
|
||||||
}
|
}
|
||||||
|
|
||||||
public TaskResult result(DiscoveryNode node, Exception error) throws IOException {
|
public TaskResult result(DiscoveryNode node, Exception error) throws IOException {
|
||||||
return new TaskResult(taskInfo(node.getId(), true), error);
|
return new TaskResult(taskInfo(node.getId(), true, true), error);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TaskResult result(DiscoveryNode node, ActionResponse response) throws IOException {
|
public TaskResult result(DiscoveryNode node, ActionResponse response) throws IOException {
|
||||||
if (response instanceof ToXContent) {
|
if (response instanceof ToXContent) {
|
||||||
return new TaskResult(taskInfo(node.getId(), true), (ToXContent) response);
|
return new TaskResult(taskInfo(node.getId(), true, true), (ToXContent) response);
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("response has to implement ToXContent to be able to store the results");
|
throw new IllegalStateException("response has to implement ToXContent to be able to store the results");
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,8 @@ public final class TaskInfo implements Writeable, ToXContentFragment {
|
||||||
|
|
||||||
private final Map<String, String> headers;
|
private final Map<String, String> headers;
|
||||||
|
|
||||||
|
private final TaskResourceStats resourceStats;
|
||||||
|
|
||||||
public TaskInfo(
|
public TaskInfo(
|
||||||
TaskId taskId,
|
TaskId taskId,
|
||||||
String type,
|
String type,
|
||||||
|
@ -97,7 +99,8 @@ public final class TaskInfo implements Writeable, ToXContentFragment {
|
||||||
boolean cancellable,
|
boolean cancellable,
|
||||||
boolean cancelled,
|
boolean cancelled,
|
||||||
TaskId parentTaskId,
|
TaskId parentTaskId,
|
||||||
Map<String, String> headers
|
Map<String, String> headers,
|
||||||
|
TaskResourceStats resourceStats
|
||||||
) {
|
) {
|
||||||
if (cancellable == false && cancelled == true) {
|
if (cancellable == false && cancelled == true) {
|
||||||
throw new IllegalArgumentException("task cannot be cancelled");
|
throw new IllegalArgumentException("task cannot be cancelled");
|
||||||
|
@ -113,11 +116,13 @@ public final class TaskInfo implements Writeable, ToXContentFragment {
|
||||||
this.cancelled = cancelled;
|
this.cancelled = cancelled;
|
||||||
this.parentTaskId = parentTaskId;
|
this.parentTaskId = parentTaskId;
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
|
this.resourceStats = resourceStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read from a stream.
|
* Read from a stream.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public TaskInfo(StreamInput in) throws IOException {
|
public TaskInfo(StreamInput in) throws IOException {
|
||||||
taskId = TaskId.readFromStream(in);
|
taskId = TaskId.readFromStream(in);
|
||||||
type = in.readString();
|
type = in.readString();
|
||||||
|
@ -137,6 +142,11 @@ public final class TaskInfo implements Writeable, ToXContentFragment {
|
||||||
}
|
}
|
||||||
parentTaskId = TaskId.readFromStream(in);
|
parentTaskId = TaskId.readFromStream(in);
|
||||||
headers = in.readMap(StreamInput::readString, StreamInput::readString);
|
headers = in.readMap(StreamInput::readString, StreamInput::readString);
|
||||||
|
if (in.getVersion().onOrAfter(Version.V_2_0_0)) {
|
||||||
|
resourceStats = in.readOptionalWriteable(TaskResourceStats::new);
|
||||||
|
} else {
|
||||||
|
resourceStats = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -154,6 +164,9 @@ public final class TaskInfo implements Writeable, ToXContentFragment {
|
||||||
}
|
}
|
||||||
parentTaskId.writeTo(out);
|
parentTaskId.writeTo(out);
|
||||||
out.writeMap(headers, StreamOutput::writeString, StreamOutput::writeString);
|
out.writeMap(headers, StreamOutput::writeString, StreamOutput::writeString);
|
||||||
|
if (out.getVersion().onOrAfter(Version.V_2_0_0)) {
|
||||||
|
out.writeOptionalWriteable(resourceStats);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TaskId getTaskId() {
|
public TaskId getTaskId() {
|
||||||
|
@ -226,6 +239,13 @@ public final class TaskInfo implements Writeable, ToXContentFragment {
|
||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the task resource information
|
||||||
|
*/
|
||||||
|
public TaskResourceStats getResourceStats() {
|
||||||
|
return resourceStats;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
builder.field("node", taskId.getNodeId());
|
builder.field("node", taskId.getNodeId());
|
||||||
|
@ -253,6 +273,11 @@ public final class TaskInfo implements Writeable, ToXContentFragment {
|
||||||
builder.field(attribute.getKey(), attribute.getValue());
|
builder.field(attribute.getKey(), attribute.getValue());
|
||||||
}
|
}
|
||||||
builder.endObject();
|
builder.endObject();
|
||||||
|
if (resourceStats != null) {
|
||||||
|
builder.startObject("resource_stats");
|
||||||
|
resourceStats.toXContent(builder, params);
|
||||||
|
builder.endObject();
|
||||||
|
}
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,6 +303,8 @@ public final class TaskInfo implements Writeable, ToXContentFragment {
|
||||||
// This might happen if we are reading an old version of task info
|
// This might happen if we are reading an old version of task info
|
||||||
headers = Collections.emptyMap();
|
headers = Collections.emptyMap();
|
||||||
}
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
TaskResourceStats resourceStats = (TaskResourceStats) a[i++];
|
||||||
RawTaskStatus status = statusBytes == null ? null : new RawTaskStatus(statusBytes);
|
RawTaskStatus status = statusBytes == null ? null : new RawTaskStatus(statusBytes);
|
||||||
TaskId parentTaskId = parentTaskIdString == null ? TaskId.EMPTY_TASK_ID : new TaskId(parentTaskIdString);
|
TaskId parentTaskId = parentTaskIdString == null ? TaskId.EMPTY_TASK_ID : new TaskId(parentTaskIdString);
|
||||||
return new TaskInfo(
|
return new TaskInfo(
|
||||||
|
@ -291,7 +318,8 @@ public final class TaskInfo implements Writeable, ToXContentFragment {
|
||||||
cancellable,
|
cancellable,
|
||||||
cancelled,
|
cancelled,
|
||||||
parentTaskId,
|
parentTaskId,
|
||||||
headers
|
headers,
|
||||||
|
resourceStats
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
static {
|
static {
|
||||||
|
@ -309,6 +337,7 @@ public final class TaskInfo implements Writeable, ToXContentFragment {
|
||||||
PARSER.declareBoolean(optionalConstructorArg(), new ParseField("cancelled"));
|
PARSER.declareBoolean(optionalConstructorArg(), new ParseField("cancelled"));
|
||||||
PARSER.declareString(optionalConstructorArg(), new ParseField("parent_task_id"));
|
PARSER.declareString(optionalConstructorArg(), new ParseField("parent_task_id"));
|
||||||
PARSER.declareObject(optionalConstructorArg(), (p, c) -> p.mapStrings(), new ParseField("headers"));
|
PARSER.declareObject(optionalConstructorArg(), (p, c) -> p.mapStrings(), new ParseField("headers"));
|
||||||
|
PARSER.declareObject(optionalConstructorArg(), (p, c) -> TaskResourceStats.fromXContent(p), new ParseField("resource_stats"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -333,7 +362,8 @@ public final class TaskInfo implements Writeable, ToXContentFragment {
|
||||||
&& Objects.equals(cancellable, other.cancellable)
|
&& Objects.equals(cancellable, other.cancellable)
|
||||||
&& Objects.equals(cancelled, other.cancelled)
|
&& Objects.equals(cancelled, other.cancelled)
|
||||||
&& Objects.equals(status, other.status)
|
&& Objects.equals(status, other.status)
|
||||||
&& Objects.equals(headers, other.headers);
|
&& Objects.equals(headers, other.headers)
|
||||||
|
&& Objects.equals(resourceStats, other.resourceStats);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -349,7 +379,8 @@ public final class TaskInfo implements Writeable, ToXContentFragment {
|
||||||
cancellable,
|
cancellable,
|
||||||
cancelled,
|
cancelled,
|
||||||
status,
|
status,
|
||||||
headers
|
headers,
|
||||||
|
resourceStats
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* The OpenSearch Contributors require contributions made to
|
||||||
|
* this file be licensed under the Apache-2.0 license or a
|
||||||
|
* compatible open source license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.opensearch.tasks;
|
||||||
|
|
||||||
|
import org.opensearch.common.Strings;
|
||||||
|
import org.opensearch.common.io.stream.StreamInput;
|
||||||
|
import org.opensearch.common.io.stream.StreamOutput;
|
||||||
|
import org.opensearch.common.io.stream.Writeable;
|
||||||
|
import org.opensearch.common.xcontent.ToXContentFragment;
|
||||||
|
import org.opensearch.common.xcontent.XContentBuilder;
|
||||||
|
import org.opensearch.common.xcontent.XContentParser;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource information about a currently running task.
|
||||||
|
* <p>
|
||||||
|
* Writeable TaskResourceStats objects are used to represent resource
|
||||||
|
* snapshot information about currently running task.
|
||||||
|
*/
|
||||||
|
public class TaskResourceStats implements Writeable, ToXContentFragment {
|
||||||
|
private final Map<String, TaskResourceUsage> resourceUsage;
|
||||||
|
|
||||||
|
public TaskResourceStats(Map<String, TaskResourceUsage> resourceUsage) {
|
||||||
|
this.resourceUsage = Objects.requireNonNull(resourceUsage, "resource usage is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read from a stream.
|
||||||
|
*/
|
||||||
|
public TaskResourceStats(StreamInput in) throws IOException {
|
||||||
|
resourceUsage = in.readMap(StreamInput::readString, TaskResourceUsage::readFromStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
|
out.writeMap(resourceUsage, StreamOutput::writeString, (stream, stats) -> stats.writeTo(stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, TaskResourceUsage> getResourceUsageInfo() {
|
||||||
|
return resourceUsage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
|
for (Map.Entry<String, TaskResourceUsage> resourceUsageEntry : resourceUsage.entrySet()) {
|
||||||
|
builder.startObject(resourceUsageEntry.getKey());
|
||||||
|
if (resourceUsageEntry.getValue() != null) {
|
||||||
|
resourceUsageEntry.getValue().toXContent(builder, params);
|
||||||
|
}
|
||||||
|
builder.endObject();
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TaskResourceStats fromXContent(XContentParser parser) throws IOException {
|
||||||
|
XContentParser.Token token = parser.currentToken();
|
||||||
|
if (token == null) {
|
||||||
|
token = parser.nextToken();
|
||||||
|
}
|
||||||
|
if (token == XContentParser.Token.START_OBJECT) {
|
||||||
|
token = parser.nextToken();
|
||||||
|
}
|
||||||
|
final Map<String, TaskResourceUsage> resourceStats = new HashMap<>();
|
||||||
|
if (token == XContentParser.Token.FIELD_NAME) {
|
||||||
|
assert parser.currentToken() == XContentParser.Token.FIELD_NAME : "Expected field name but saw [" + parser.currentToken() + "]";
|
||||||
|
do {
|
||||||
|
// Must point to field name
|
||||||
|
String fieldName = parser.currentName();
|
||||||
|
// And then the value
|
||||||
|
TaskResourceUsage value = TaskResourceUsage.fromXContent(parser);
|
||||||
|
resourceStats.put(fieldName, value);
|
||||||
|
} while (parser.nextToken() == XContentParser.Token.FIELD_NAME);
|
||||||
|
}
|
||||||
|
return new TaskResourceStats(resourceStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Strings.toString(this, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements equals and hashcode for testing
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == null || obj.getClass() != TaskResourceStats.class) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TaskResourceStats other = (TaskResourceStats) obj;
|
||||||
|
return Objects.equals(resourceUsage, other.resourceUsage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(resourceUsage);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* The OpenSearch Contributors require contributions made to
|
||||||
|
* this file be licensed under the Apache-2.0 license or a
|
||||||
|
* compatible open source license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.opensearch.tasks;
|
||||||
|
|
||||||
|
import org.opensearch.common.ParseField;
|
||||||
|
import org.opensearch.common.Strings;
|
||||||
|
import org.opensearch.common.io.stream.StreamInput;
|
||||||
|
import org.opensearch.common.io.stream.StreamOutput;
|
||||||
|
import org.opensearch.common.io.stream.Writeable;
|
||||||
|
import org.opensearch.common.xcontent.ConstructingObjectParser;
|
||||||
|
import org.opensearch.common.xcontent.ToXContentFragment;
|
||||||
|
import org.opensearch.common.xcontent.XContentBuilder;
|
||||||
|
import org.opensearch.common.xcontent.XContentParser;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task resource usage information
|
||||||
|
* <p>
|
||||||
|
* Writeable TaskResourceUsage objects are used to represent resource usage
|
||||||
|
* information of running tasks.
|
||||||
|
*/
|
||||||
|
public class TaskResourceUsage implements Writeable, ToXContentFragment {
|
||||||
|
private static final ParseField CPU_TIME_IN_NANOS = new ParseField("cpu_time_in_nanos");
|
||||||
|
private static final ParseField MEMORY_IN_BYTES = new ParseField("memory_in_bytes");
|
||||||
|
|
||||||
|
private final long cpuTimeInNanos;
|
||||||
|
private final long memoryInBytes;
|
||||||
|
|
||||||
|
public TaskResourceUsage(long cpuTimeInNanos, long memoryInBytes) {
|
||||||
|
this.cpuTimeInNanos = cpuTimeInNanos;
|
||||||
|
this.memoryInBytes = memoryInBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read from a stream.
|
||||||
|
*/
|
||||||
|
public static TaskResourceUsage readFromStream(StreamInput in) throws IOException {
|
||||||
|
return new TaskResourceUsage(in.readVLong(), in.readVLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
|
out.writeVLong(cpuTimeInNanos);
|
||||||
|
out.writeVLong(memoryInBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCpuTimeInNanos() {
|
||||||
|
return cpuTimeInNanos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getMemoryInBytes() {
|
||||||
|
return memoryInBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
|
builder.field(CPU_TIME_IN_NANOS.getPreferredName(), cpuTimeInNanos);
|
||||||
|
builder.field(MEMORY_IN_BYTES.getPreferredName(), memoryInBytes);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final ConstructingObjectParser<TaskResourceUsage, Void> PARSER = new ConstructingObjectParser<>(
|
||||||
|
"task_resource_usage",
|
||||||
|
a -> new TaskResourceUsage((Long) a[0], (Long) a[1])
|
||||||
|
);
|
||||||
|
|
||||||
|
static {
|
||||||
|
PARSER.declareLong(constructorArg(), CPU_TIME_IN_NANOS);
|
||||||
|
PARSER.declareLong(constructorArg(), MEMORY_IN_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TaskResourceUsage fromXContent(XContentParser parser) {
|
||||||
|
return PARSER.apply(parser, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Strings.toString(this, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements equals and hashcode for testing
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == null || obj.getClass() != TaskResourceUsage.class) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TaskResourceUsage other = (TaskResourceUsage) obj;
|
||||||
|
return Objects.equals(cpuTimeInNanos, other.cpuTimeInNanos) && Objects.equals(memoryInBytes, other.memoryInBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(cpuTimeInNanos, memoryInBytes);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* The OpenSearch Contributors require contributions made to
|
||||||
|
* this file be licensed under the Apache-2.0 license or a
|
||||||
|
* compatible open source license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.opensearch.tasks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource consumption information about a particular execution of thread.
|
||||||
|
* <p>
|
||||||
|
* It captures the resource usage information about a particular execution of thread
|
||||||
|
* for a specific stats type like worker_stats or response_stats etc.,
|
||||||
|
*/
|
||||||
|
public class ThreadResourceInfo {
|
||||||
|
private volatile boolean isActive = true;
|
||||||
|
private final ResourceStatsType statsType;
|
||||||
|
private final ResourceUsageInfo resourceUsageInfo;
|
||||||
|
|
||||||
|
public ThreadResourceInfo(ResourceStatsType statsType, ResourceUsageMetric... resourceUsageMetrics) {
|
||||||
|
this.statsType = statsType;
|
||||||
|
this.resourceUsageInfo = new ResourceUsageInfo(resourceUsageMetrics);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates thread's resource consumption information.
|
||||||
|
*/
|
||||||
|
public void recordResourceUsageMetrics(ResourceUsageMetric... resourceUsageMetrics) {
|
||||||
|
resourceUsageInfo.recordResourceUsageMetrics(resourceUsageMetrics);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActive(boolean isActive) {
|
||||||
|
this.isActive = isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActive() {
|
||||||
|
return isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceStatsType getStatsType() {
|
||||||
|
return statsType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceUsageInfo getResourceUsageInfo() {
|
||||||
|
return resourceUsageInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return resourceUsageInfo + ", stats_type=" + statsType + ", is_active=" + isActive;
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,16 +31,23 @@
|
||||||
|
|
||||||
package org.opensearch.action.admin.cluster.node.tasks;
|
package org.opensearch.action.admin.cluster.node.tasks;
|
||||||
|
|
||||||
|
import org.opensearch.action.search.SearchAction;
|
||||||
import org.opensearch.common.bytes.BytesArray;
|
import org.opensearch.common.bytes.BytesArray;
|
||||||
import org.opensearch.common.xcontent.XContentHelper;
|
import org.opensearch.common.xcontent.XContentHelper;
|
||||||
|
import org.opensearch.tasks.Task;
|
||||||
import org.opensearch.tasks.TaskId;
|
import org.opensearch.tasks.TaskId;
|
||||||
import org.opensearch.tasks.TaskInfo;
|
import org.opensearch.tasks.TaskInfo;
|
||||||
|
import org.opensearch.tasks.ResourceUsageMetric;
|
||||||
|
import org.opensearch.tasks.ResourceStats;
|
||||||
|
import org.opensearch.tasks.ResourceStatsType;
|
||||||
import org.opensearch.test.OpenSearchTestCase;
|
import org.opensearch.test.OpenSearchTestCase;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.opensearch.tasks.TaskInfoTests.randomResourceStats;
|
||||||
|
|
||||||
public class TaskTests extends OpenSearchTestCase {
|
public class TaskTests extends OpenSearchTestCase {
|
||||||
|
|
||||||
public void testTaskInfoToString() {
|
public void testTaskInfoToString() {
|
||||||
|
@ -61,7 +68,8 @@ public class TaskTests extends OpenSearchTestCase {
|
||||||
cancellable,
|
cancellable,
|
||||||
cancelled,
|
cancelled,
|
||||||
TaskId.EMPTY_TASK_ID,
|
TaskId.EMPTY_TASK_ID,
|
||||||
Collections.singletonMap("foo", "bar")
|
Collections.singletonMap("foo", "bar"),
|
||||||
|
randomResourceStats(randomBoolean())
|
||||||
);
|
);
|
||||||
String taskInfoString = taskInfo.toString();
|
String taskInfoString = taskInfo.toString();
|
||||||
Map<String, Object> map = XContentHelper.convertToMap(new BytesArray(taskInfoString.getBytes(StandardCharsets.UTF_8)), true).v2();
|
Map<String, Object> map = XContentHelper.convertToMap(new BytesArray(taskInfoString.getBytes(StandardCharsets.UTF_8)), true).v2();
|
||||||
|
@ -94,7 +102,8 @@ public class TaskTests extends OpenSearchTestCase {
|
||||||
cancellable,
|
cancellable,
|
||||||
cancelled,
|
cancelled,
|
||||||
TaskId.EMPTY_TASK_ID,
|
TaskId.EMPTY_TASK_ID,
|
||||||
Collections.singletonMap("foo", "bar")
|
Collections.singletonMap("foo", "bar"),
|
||||||
|
randomResourceStats(randomBoolean())
|
||||||
);
|
);
|
||||||
String taskInfoString = taskInfo.toString();
|
String taskInfoString = taskInfo.toString();
|
||||||
Map<String, Object> map = XContentHelper.convertToMap(new BytesArray(taskInfoString.getBytes(StandardCharsets.UTF_8)), true).v2();
|
Map<String, Object> map = XContentHelper.convertToMap(new BytesArray(taskInfoString.getBytes(StandardCharsets.UTF_8)), true).v2();
|
||||||
|
@ -120,7 +129,8 @@ public class TaskTests extends OpenSearchTestCase {
|
||||||
cancellable,
|
cancellable,
|
||||||
cancelled,
|
cancelled,
|
||||||
TaskId.EMPTY_TASK_ID,
|
TaskId.EMPTY_TASK_ID,
|
||||||
Collections.singletonMap("foo", "bar")
|
Collections.singletonMap("foo", "bar"),
|
||||||
|
randomResourceStats(randomBoolean())
|
||||||
);
|
);
|
||||||
String taskInfoString = taskInfo.toString();
|
String taskInfoString = taskInfo.toString();
|
||||||
Map<String, Object> map = XContentHelper.convertToMap(new BytesArray(taskInfoString.getBytes(StandardCharsets.UTF_8)), true).v2();
|
Map<String, Object> map = XContentHelper.convertToMap(new BytesArray(taskInfoString.getBytes(StandardCharsets.UTF_8)), true).v2();
|
||||||
|
@ -148,9 +158,75 @@ public class TaskTests extends OpenSearchTestCase {
|
||||||
cancellable,
|
cancellable,
|
||||||
cancelled,
|
cancelled,
|
||||||
TaskId.EMPTY_TASK_ID,
|
TaskId.EMPTY_TASK_ID,
|
||||||
Collections.singletonMap("foo", "bar")
|
Collections.singletonMap("foo", "bar"),
|
||||||
|
randomResourceStats(randomBoolean())
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
assertEquals(e.getMessage(), "task cannot be cancelled");
|
assertEquals(e.getMessage(), "task cannot be cancelled");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testTaskResourceStats() {
|
||||||
|
final Task task = new Task(
|
||||||
|
randomLong(),
|
||||||
|
"transport",
|
||||||
|
SearchAction.NAME,
|
||||||
|
"description",
|
||||||
|
new TaskId(randomLong() + ":" + randomLong()),
|
||||||
|
Collections.emptyMap()
|
||||||
|
);
|
||||||
|
|
||||||
|
long totalMemory = 0L;
|
||||||
|
long totalCPU = 0L;
|
||||||
|
|
||||||
|
// reporting resource consumption events and checking total consumption values
|
||||||
|
for (int i = 0; i < randomInt(10); i++) {
|
||||||
|
long initial_memory = randomLongBetween(1, 100);
|
||||||
|
long initial_cpu = randomLongBetween(1, 100);
|
||||||
|
|
||||||
|
ResourceUsageMetric[] initialTaskResourceMetrics = new ResourceUsageMetric[] {
|
||||||
|
new ResourceUsageMetric(ResourceStats.MEMORY, initial_memory),
|
||||||
|
new ResourceUsageMetric(ResourceStats.CPU, initial_cpu) };
|
||||||
|
task.startThreadResourceTracking(i, ResourceStatsType.WORKER_STATS, initialTaskResourceMetrics);
|
||||||
|
|
||||||
|
long memory = initial_memory + randomLongBetween(1, 10000);
|
||||||
|
long cpu = initial_cpu + randomLongBetween(1, 10000);
|
||||||
|
|
||||||
|
totalMemory += memory - initial_memory;
|
||||||
|
totalCPU += cpu - initial_cpu;
|
||||||
|
|
||||||
|
ResourceUsageMetric[] taskResourceMetrics = new ResourceUsageMetric[] {
|
||||||
|
new ResourceUsageMetric(ResourceStats.MEMORY, memory),
|
||||||
|
new ResourceUsageMetric(ResourceStats.CPU, cpu) };
|
||||||
|
task.updateThreadResourceStats(i, ResourceStatsType.WORKER_STATS, taskResourceMetrics);
|
||||||
|
task.stopThreadResourceTracking(i, ResourceStatsType.WORKER_STATS);
|
||||||
|
}
|
||||||
|
assertEquals(task.getTotalResourceStats().getMemoryInBytes(), totalMemory);
|
||||||
|
assertEquals(task.getTotalResourceStats().getCpuTimeInNanos(), totalCPU);
|
||||||
|
|
||||||
|
// updating should throw an IllegalStateException when active entry is not present.
|
||||||
|
try {
|
||||||
|
task.updateThreadResourceStats(randomInt(), ResourceStatsType.WORKER_STATS);
|
||||||
|
fail("update should not be successful as active entry is not present!");
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-adding a thread entry that is already present, should throw an exception
|
||||||
|
int threadId = randomInt();
|
||||||
|
task.startThreadResourceTracking(threadId, ResourceStatsType.WORKER_STATS, new ResourceUsageMetric(ResourceStats.MEMORY, 100));
|
||||||
|
try {
|
||||||
|
task.startThreadResourceTracking(threadId, ResourceStatsType.WORKER_STATS);
|
||||||
|
fail("add/start should not be successful as active entry is already present!");
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
|
||||||
|
// existing active entry is present only for memory, update cannot be called with cpu values.
|
||||||
|
try {
|
||||||
|
task.updateThreadResourceStats(threadId, ResourceStatsType.WORKER_STATS, new ResourceUsageMetric(ResourceStats.CPU, 200));
|
||||||
|
fail("update should not be successful as entry for CPU is not present!");
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ public class CancelTasksResponseTests extends AbstractXContentTestCase<CancelTas
|
||||||
private static List<TaskInfo> randomTasks() {
|
private static List<TaskInfo> randomTasks() {
|
||||||
List<TaskInfo> randomTasks = new ArrayList<>();
|
List<TaskInfo> randomTasks = new ArrayList<>();
|
||||||
for (int i = 0; i < randomInt(10); i++) {
|
for (int i = 0; i < randomInt(10); i++) {
|
||||||
randomTasks.add(TaskInfoTests.randomTaskInfo());
|
randomTasks.add(TaskInfoTests.randomTaskInfo(false));
|
||||||
}
|
}
|
||||||
return randomTasks;
|
return randomTasks;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ import java.io.IOException;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
@ -72,7 +73,12 @@ public class ListTasksResponseTests extends AbstractXContentTestCase<ListTasksRe
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
new TaskId("node1", 0),
|
new TaskId("node1", 0),
|
||||||
Collections.singletonMap("foo", "bar")
|
Collections.singletonMap("foo", "bar"),
|
||||||
|
new TaskResourceStats(new HashMap<String, TaskResourceUsage>() {
|
||||||
|
{
|
||||||
|
put("dummy-type1", new TaskResourceUsage(100, 100));
|
||||||
|
}
|
||||||
|
})
|
||||||
);
|
);
|
||||||
ListTasksResponse tasksResponse = new ListTasksResponse(singletonList(info), emptyList(), emptyList());
|
ListTasksResponse tasksResponse = new ListTasksResponse(singletonList(info), emptyList(), emptyList());
|
||||||
assertEquals(
|
assertEquals(
|
||||||
|
@ -93,6 +99,12 @@ public class ListTasksResponseTests extends AbstractXContentTestCase<ListTasksRe
|
||||||
+ " \"parent_task_id\" : \"node1:0\",\n"
|
+ " \"parent_task_id\" : \"node1:0\",\n"
|
||||||
+ " \"headers\" : {\n"
|
+ " \"headers\" : {\n"
|
||||||
+ " \"foo\" : \"bar\"\n"
|
+ " \"foo\" : \"bar\"\n"
|
||||||
|
+ " },\n"
|
||||||
|
+ " \"resource_stats\" : {\n"
|
||||||
|
+ " \"dummy-type1\" : {\n"
|
||||||
|
+ " \"cpu_time_in_nanos\" : 100,\n"
|
||||||
|
+ " \"memory_in_bytes\" : 100\n"
|
||||||
|
+ " }\n"
|
||||||
+ " }\n"
|
+ " }\n"
|
||||||
+ " }\n"
|
+ " }\n"
|
||||||
+ " ]\n"
|
+ " ]\n"
|
||||||
|
@ -127,8 +139,8 @@ public class ListTasksResponseTests extends AbstractXContentTestCase<ListTasksRe
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Predicate<String> getRandomFieldsExcludeFilter() {
|
protected Predicate<String> getRandomFieldsExcludeFilter() {
|
||||||
// status and headers hold arbitrary content, we can't inject random fields in them
|
// status, headers and resource_stats hold arbitrary content, we can't inject random fields in them
|
||||||
return field -> field.endsWith("status") || field.endsWith("headers");
|
return field -> field.endsWith("status") || field.endsWith("headers") || field.contains("resource_stats");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -77,13 +77,13 @@ public class TaskInfoTests extends AbstractSerializingTestCase<TaskInfo> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Predicate<String> getRandomFieldsExcludeFilter() {
|
protected Predicate<String> getRandomFieldsExcludeFilter() {
|
||||||
// status and headers hold arbitrary content, we can't inject random fields in them
|
// status, headers and resource_stats hold arbitrary content, we can't inject random fields in them
|
||||||
return field -> "status".equals(field) || "headers".equals(field);
|
return field -> "status".equals(field) || "headers".equals(field) || field.contains("resource_stats");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TaskInfo mutateInstance(TaskInfo info) {
|
protected TaskInfo mutateInstance(TaskInfo info) {
|
||||||
switch (between(0, 9)) {
|
switch (between(0, 10)) {
|
||||||
case 0:
|
case 0:
|
||||||
TaskId taskId = new TaskId(info.getTaskId().getNodeId() + randomAlphaOfLength(5), info.getTaskId().getId());
|
TaskId taskId = new TaskId(info.getTaskId().getNodeId() + randomAlphaOfLength(5), info.getTaskId().getId());
|
||||||
return new TaskInfo(
|
return new TaskInfo(
|
||||||
|
@ -97,7 +97,8 @@ public class TaskInfoTests extends AbstractSerializingTestCase<TaskInfo> {
|
||||||
info.isCancellable(),
|
info.isCancellable(),
|
||||||
info.isCancelled(),
|
info.isCancelled(),
|
||||||
info.getParentTaskId(),
|
info.getParentTaskId(),
|
||||||
info.getHeaders()
|
info.getHeaders(),
|
||||||
|
info.getResourceStats()
|
||||||
);
|
);
|
||||||
case 1:
|
case 1:
|
||||||
return new TaskInfo(
|
return new TaskInfo(
|
||||||
|
@ -111,7 +112,8 @@ public class TaskInfoTests extends AbstractSerializingTestCase<TaskInfo> {
|
||||||
info.isCancellable(),
|
info.isCancellable(),
|
||||||
info.isCancelled(),
|
info.isCancelled(),
|
||||||
info.getParentTaskId(),
|
info.getParentTaskId(),
|
||||||
info.getHeaders()
|
info.getHeaders(),
|
||||||
|
info.getResourceStats()
|
||||||
);
|
);
|
||||||
case 2:
|
case 2:
|
||||||
return new TaskInfo(
|
return new TaskInfo(
|
||||||
|
@ -125,7 +127,8 @@ public class TaskInfoTests extends AbstractSerializingTestCase<TaskInfo> {
|
||||||
info.isCancellable(),
|
info.isCancellable(),
|
||||||
info.isCancelled(),
|
info.isCancelled(),
|
||||||
info.getParentTaskId(),
|
info.getParentTaskId(),
|
||||||
info.getHeaders()
|
info.getHeaders(),
|
||||||
|
info.getResourceStats()
|
||||||
);
|
);
|
||||||
case 3:
|
case 3:
|
||||||
return new TaskInfo(
|
return new TaskInfo(
|
||||||
|
@ -139,7 +142,8 @@ public class TaskInfoTests extends AbstractSerializingTestCase<TaskInfo> {
|
||||||
info.isCancellable(),
|
info.isCancellable(),
|
||||||
info.isCancelled(),
|
info.isCancelled(),
|
||||||
info.getParentTaskId(),
|
info.getParentTaskId(),
|
||||||
info.getHeaders()
|
info.getHeaders(),
|
||||||
|
info.getResourceStats()
|
||||||
);
|
);
|
||||||
case 4:
|
case 4:
|
||||||
Task.Status newStatus = randomValueOtherThan(info.getStatus(), TaskInfoTests::randomRawTaskStatus);
|
Task.Status newStatus = randomValueOtherThan(info.getStatus(), TaskInfoTests::randomRawTaskStatus);
|
||||||
|
@ -154,7 +158,8 @@ public class TaskInfoTests extends AbstractSerializingTestCase<TaskInfo> {
|
||||||
info.isCancellable(),
|
info.isCancellable(),
|
||||||
info.isCancelled(),
|
info.isCancelled(),
|
||||||
info.getParentTaskId(),
|
info.getParentTaskId(),
|
||||||
info.getHeaders()
|
info.getHeaders(),
|
||||||
|
info.getResourceStats()
|
||||||
);
|
);
|
||||||
case 5:
|
case 5:
|
||||||
return new TaskInfo(
|
return new TaskInfo(
|
||||||
|
@ -168,7 +173,8 @@ public class TaskInfoTests extends AbstractSerializingTestCase<TaskInfo> {
|
||||||
info.isCancellable(),
|
info.isCancellable(),
|
||||||
info.isCancelled(),
|
info.isCancelled(),
|
||||||
info.getParentTaskId(),
|
info.getParentTaskId(),
|
||||||
info.getHeaders()
|
info.getHeaders(),
|
||||||
|
info.getResourceStats()
|
||||||
);
|
);
|
||||||
case 6:
|
case 6:
|
||||||
return new TaskInfo(
|
return new TaskInfo(
|
||||||
|
@ -182,7 +188,8 @@ public class TaskInfoTests extends AbstractSerializingTestCase<TaskInfo> {
|
||||||
info.isCancellable(),
|
info.isCancellable(),
|
||||||
info.isCancelled(),
|
info.isCancelled(),
|
||||||
info.getParentTaskId(),
|
info.getParentTaskId(),
|
||||||
info.getHeaders()
|
info.getHeaders(),
|
||||||
|
info.getResourceStats()
|
||||||
);
|
);
|
||||||
case 7:
|
case 7:
|
||||||
return new TaskInfo(
|
return new TaskInfo(
|
||||||
|
@ -196,7 +203,8 @@ public class TaskInfoTests extends AbstractSerializingTestCase<TaskInfo> {
|
||||||
info.isCancellable() == false,
|
info.isCancellable() == false,
|
||||||
false,
|
false,
|
||||||
info.getParentTaskId(),
|
info.getParentTaskId(),
|
||||||
info.getHeaders()
|
info.getHeaders(),
|
||||||
|
info.getResourceStats()
|
||||||
);
|
);
|
||||||
case 8:
|
case 8:
|
||||||
TaskId parentId = new TaskId(info.getParentTaskId().getNodeId() + randomAlphaOfLength(5), info.getParentTaskId().getId());
|
TaskId parentId = new TaskId(info.getParentTaskId().getNodeId() + randomAlphaOfLength(5), info.getParentTaskId().getId());
|
||||||
|
@ -211,7 +219,8 @@ public class TaskInfoTests extends AbstractSerializingTestCase<TaskInfo> {
|
||||||
info.isCancellable(),
|
info.isCancellable(),
|
||||||
info.isCancelled(),
|
info.isCancelled(),
|
||||||
parentId,
|
parentId,
|
||||||
info.getHeaders()
|
info.getHeaders(),
|
||||||
|
info.getResourceStats()
|
||||||
);
|
);
|
||||||
case 9:
|
case 9:
|
||||||
Map<String, String> headers = info.getHeaders();
|
Map<String, String> headers = info.getHeaders();
|
||||||
|
@ -232,7 +241,30 @@ public class TaskInfoTests extends AbstractSerializingTestCase<TaskInfo> {
|
||||||
info.isCancellable(),
|
info.isCancellable(),
|
||||||
info.isCancelled(),
|
info.isCancelled(),
|
||||||
info.getParentTaskId(),
|
info.getParentTaskId(),
|
||||||
headers
|
headers,
|
||||||
|
info.getResourceStats()
|
||||||
|
);
|
||||||
|
case 10:
|
||||||
|
Map<String, TaskResourceUsage> resourceUsageMap;
|
||||||
|
if (info.getResourceStats() == null) {
|
||||||
|
resourceUsageMap = new HashMap<>(1);
|
||||||
|
} else {
|
||||||
|
resourceUsageMap = new HashMap<>(info.getResourceStats().getResourceUsageInfo());
|
||||||
|
}
|
||||||
|
resourceUsageMap.put(randomAlphaOfLength(5), new TaskResourceUsage(randomNonNegativeLong(), randomNonNegativeLong()));
|
||||||
|
return new TaskInfo(
|
||||||
|
info.getTaskId(),
|
||||||
|
info.getType(),
|
||||||
|
info.getAction(),
|
||||||
|
info.getDescription(),
|
||||||
|
info.getStatus(),
|
||||||
|
info.getStartTime(),
|
||||||
|
info.getRunningTimeNanos(),
|
||||||
|
info.isCancellable(),
|
||||||
|
info.isCancelled(),
|
||||||
|
info.getParentTaskId(),
|
||||||
|
info.getHeaders(),
|
||||||
|
new TaskResourceStats(resourceUsageMap)
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
|
@ -240,11 +272,15 @@ public class TaskInfoTests extends AbstractSerializingTestCase<TaskInfo> {
|
||||||
}
|
}
|
||||||
|
|
||||||
static TaskInfo randomTaskInfo() {
|
static TaskInfo randomTaskInfo() {
|
||||||
|
return randomTaskInfo(randomBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
static TaskInfo randomTaskInfo(boolean detailed) {
|
||||||
TaskId taskId = randomTaskId();
|
TaskId taskId = randomTaskId();
|
||||||
String type = randomAlphaOfLength(5);
|
String type = randomAlphaOfLength(5);
|
||||||
String action = randomAlphaOfLength(5);
|
String action = randomAlphaOfLength(5);
|
||||||
Task.Status status = randomBoolean() ? randomRawTaskStatus() : null;
|
Task.Status status = detailed ? randomRawTaskStatus() : null;
|
||||||
String description = randomBoolean() ? randomAlphaOfLength(5) : null;
|
String description = detailed ? randomAlphaOfLength(5) : null;
|
||||||
long startTime = randomLong();
|
long startTime = randomLong();
|
||||||
long runningTimeNanos = randomLong();
|
long runningTimeNanos = randomLong();
|
||||||
boolean cancellable = randomBoolean();
|
boolean cancellable = randomBoolean();
|
||||||
|
@ -264,7 +300,8 @@ public class TaskInfoTests extends AbstractSerializingTestCase<TaskInfo> {
|
||||||
cancellable,
|
cancellable,
|
||||||
cancelled,
|
cancelled,
|
||||||
parentTaskId,
|
parentTaskId,
|
||||||
headers
|
headers,
|
||||||
|
randomResourceStats(detailed)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,4 +322,14 @@ public class TaskInfoTests extends AbstractSerializingTestCase<TaskInfo> {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static TaskResourceStats randomResourceStats(boolean detailed) {
|
||||||
|
return detailed ? new TaskResourceStats(new HashMap<String, TaskResourceUsage>() {
|
||||||
|
{
|
||||||
|
for (int i = 0; i < randomInt(5); i++) {
|
||||||
|
put(randomAlphaOfLength(5), new TaskResourceUsage(randomNonNegativeLong(), randomNonNegativeLong()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue