fix(server): avoid loading huge task results for metadata queries (#3060)
This commit is contained in:
parent
c3f56b5e9f
commit
63c97ff93f
|
|
@ -102,12 +102,13 @@ public class TaskAPI extends API {
|
|||
limit = NO_LIMIT;
|
||||
List<Id> idList = ids.stream().map(IdGenerator::of)
|
||||
.collect(Collectors.toList());
|
||||
iter = scheduler.tasks(idList);
|
||||
iter = scheduler.tasks(idList, false);
|
||||
} else {
|
||||
if (status == null) {
|
||||
iter = scheduler.tasks(null, limit, page);
|
||||
iter = scheduler.tasks(null, limit, page, false);
|
||||
} else {
|
||||
iter = scheduler.tasks(parseStatus(status), limit, page);
|
||||
iter = scheduler.tasks(parseStatus(status), limit, page,
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,12 +137,17 @@ public class TaskAPI extends API {
|
|||
@Parameter(description = "The graph name")
|
||||
@PathParam("graph") String graph,
|
||||
@Parameter(description = "The task id")
|
||||
@PathParam("id") long id) {
|
||||
@PathParam("id") long id,
|
||||
@Parameter(description = "Whether to load task result")
|
||||
@DefaultValue("true")
|
||||
@QueryParam("with_result")
|
||||
boolean withResult) {
|
||||
LOG.debug("Graph [{}] get task: {}", graph, id);
|
||||
|
||||
TaskScheduler scheduler = graph(manager, graphSpace, graph)
|
||||
.taskScheduler();
|
||||
return scheduler.task(IdGenerator.of(id)).asMap();
|
||||
return scheduler.task(IdGenerator.of(id), withResult)
|
||||
.asMap(true, withResult);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
|
|
|
|||
|
|
@ -1326,8 +1326,14 @@ public final class HugeGraphAuthProxy implements HugeGraph {
|
|||
|
||||
@Override
|
||||
public <V> HugeTask<V> task(Id id) {
|
||||
return this.task(id, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> HugeTask<V> task(Id id, boolean withResult) {
|
||||
return verifyTaskPermission(HugePermission.READ,
|
||||
this.taskScheduler.task(id));
|
||||
this.taskScheduler.task(id,
|
||||
withResult));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1336,18 +1342,36 @@ public final class HugeGraphAuthProxy implements HugeGraph {
|
|||
this.taskScheduler.tasks(ids));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Iterator<HugeTask<V>> tasks(List<Id> ids,
|
||||
boolean withResult) {
|
||||
return verifyTaskPermission(HugePermission.READ,
|
||||
this.taskScheduler.tasks(ids,
|
||||
withResult));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Iterator<HugeTask<V>> tasks(TaskStatus status,
|
||||
long limit, String page) {
|
||||
Iterator<HugeTask<V>> tasks = this.taskScheduler.tasks(status,
|
||||
limit, page);
|
||||
limit,
|
||||
page);
|
||||
return verifyTaskPermission(HugePermission.READ, tasks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Iterator<HugeTask<V>> tasks(TaskStatus status,
|
||||
long limit, String page,
|
||||
boolean withResult) {
|
||||
Iterator<HugeTask<V>> tasks = this.taskScheduler.tasks(
|
||||
status, limit, page, withResult);
|
||||
return verifyTaskPermission(HugePermission.READ, tasks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> HugeTask<V> delete(Id id, boolean force) {
|
||||
verifyTaskPermission(HugePermission.DELETE,
|
||||
this.taskScheduler.task(id));
|
||||
this.taskScheduler.task(id, false));
|
||||
return this.taskScheduler.delete(id, force);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -306,10 +306,14 @@ public class DistributedTaskScheduler extends TaskAndResultScheduler {
|
|||
Iterator<Vertex> vertices = this.tx().queryTaskInfos(id);
|
||||
HugeVertex vertex = (HugeVertex) QueryResults.one(vertices);
|
||||
if (vertex == null) {
|
||||
this.deleteTaskResultFromTx(id);
|
||||
return null;
|
||||
}
|
||||
HugeTask<V> result = HugeTask.fromVertex(vertex);
|
||||
this.tx().removeVertex(vertex);
|
||||
HugeTask<V> result = HugeTask.fromVertex(vertex, false);
|
||||
// Keep the task vertex as a retryable tombstone until its result
|
||||
// vertex is removed; cronSchedule() can rediscover DELETING tasks.
|
||||
this.deleteTaskResultFromTx(id);
|
||||
this.tx().removeTaskVertex(vertex);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
|
@ -322,6 +326,12 @@ public class DistributedTaskScheduler extends TaskAndResultScheduler {
|
|||
this.updateStatus(id, null, TaskStatus.DELETING);
|
||||
return null;
|
||||
} else {
|
||||
HugeTask<?> task = this.taskWithoutResult(id);
|
||||
if (task != null && task.status() != TaskStatus.DELETING) {
|
||||
initTaskParams(task);
|
||||
task.overwriteStatus(TaskStatus.DELETING);
|
||||
this.save(task);
|
||||
}
|
||||
return this.deleteFromDB(id);
|
||||
}
|
||||
}
|
||||
|
|
@ -587,7 +597,7 @@ public class DistributedTaskScheduler extends TaskAndResultScheduler {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean isLockedTask(String taskId) {
|
||||
protected boolean isLockedTask(String taskId) {
|
||||
return MetaManager.instance().isLockedTask(graphSpace,
|
||||
graphName, taskId);
|
||||
}
|
||||
|
|
@ -629,7 +639,7 @@ public class DistributedTaskScheduler extends TaskAndResultScheduler {
|
|||
// 1. start task can be from schedule() & cronSchedule()
|
||||
// 2. recheck the status of task, in case one same task
|
||||
// called by both methods at same time;
|
||||
HugeTask<Object> queryTask = task(this.task.id());
|
||||
HugeTask<Object> queryTask = task(this.task.id(), false);
|
||||
if (queryTask != null &&
|
||||
!TaskStatus.NEW.equals(queryTask.status())) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ import org.apache.hugegraph.job.ComputerJob;
|
|||
import org.apache.hugegraph.job.EphemeralJob;
|
||||
import org.apache.hugegraph.job.GremlinJob;
|
||||
import org.apache.hugegraph.job.schema.SchemaJob;
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.type.define.SerialEnum;
|
||||
import org.apache.hugegraph.util.Blob;
|
||||
import org.apache.hugegraph.util.E;
|
||||
|
|
@ -653,6 +654,11 @@ public class HugeTask<V> extends FutureTask<V> {
|
|||
}
|
||||
|
||||
public synchronized Map<String, Object> asMap(boolean withDetails) {
|
||||
return this.asMap(withDetails, true);
|
||||
}
|
||||
|
||||
public synchronized Map<String, Object> asMap(boolean withDetails,
|
||||
boolean withResult) {
|
||||
E.checkState(this.type != null, "Task type can't be null");
|
||||
E.checkState(this.name != null, "Task name can't be null");
|
||||
|
||||
|
|
@ -689,7 +695,7 @@ public class HugeTask<V> extends FutureTask<V> {
|
|||
if (this.input != null) {
|
||||
map.put(Hidden.unHide(P.INPUT), this.input);
|
||||
}
|
||||
if (this.result != null) {
|
||||
if (withResult && this.result != null) {
|
||||
map.put(Hidden.unHide(P.RESULT), this.result);
|
||||
}
|
||||
}
|
||||
|
|
@ -697,7 +703,37 @@ public class HugeTask<V> extends FutureTask<V> {
|
|||
return map;
|
||||
}
|
||||
|
||||
synchronized HugeTask<V> copyWithoutResult() {
|
||||
HugeTask<V> task = new HugeTask<>(this.id, this.parent, this.callable);
|
||||
task.type = this.type;
|
||||
task.name = this.name;
|
||||
task.dependencies = this.dependencies == null ?
|
||||
null : InsertionOrderUtil.newSet(this.dependencies);
|
||||
task.description = this.description;
|
||||
task.context = this.context;
|
||||
task.create = this.create;
|
||||
task.server = this.server;
|
||||
task.load = this.load;
|
||||
task.status = this.status;
|
||||
task.progress = this.progress;
|
||||
task.update = this.update;
|
||||
task.retries = this.retries;
|
||||
task.input = this.input;
|
||||
task.result = null;
|
||||
task.scheduler = this.scheduler;
|
||||
return task;
|
||||
}
|
||||
|
||||
public static <V> HugeTask<V> fromVertex(Vertex vertex) {
|
||||
return fromVertex(vertex, true);
|
||||
}
|
||||
|
||||
public static <V> HugeTask<V> fromVertex(Vertex vertex,
|
||||
boolean withResult) {
|
||||
if (!withResult && vertex instanceof HugeVertex) {
|
||||
return fromHugeVertex((HugeVertex) vertex);
|
||||
}
|
||||
|
||||
String callableName = vertex.value(P.CALLABLE);
|
||||
TaskCallable<V> callable;
|
||||
try {
|
||||
|
|
@ -710,11 +746,37 @@ public class HugeTask<V> extends FutureTask<V> {
|
|||
for (Iterator<VertexProperty<Object>> iter = vertex.properties();
|
||||
iter.hasNext(); ) {
|
||||
VertexProperty<Object> prop = iter.next();
|
||||
if (!withResult && P.RESULT.equals(prop.key())) {
|
||||
continue;
|
||||
}
|
||||
task.property(prop.key(), prop.value());
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
private static <V> HugeTask<V> fromHugeVertex(HugeVertex vertex) {
|
||||
String callableName = getPropertyValue(vertex, P.CALLABLE);
|
||||
TaskCallable<V> callable;
|
||||
try {
|
||||
callable = TaskCallable.fromClass(callableName);
|
||||
} catch (Exception e) {
|
||||
callable = TaskCallable.empty(e);
|
||||
}
|
||||
|
||||
HugeTask<V> task = new HugeTask<>(vertex.id(), null, callable);
|
||||
for (String property : P.METADATA_PROPERTIES) {
|
||||
Object value = getPropertyValue(vertex, property);
|
||||
if (value != null) {
|
||||
task.property(property, value);
|
||||
}
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
private static <V> V getPropertyValue(HugeVertex vertex, String property) {
|
||||
return vertex.getPropertyValue(vertex.graph().propertyKey(property).id());
|
||||
}
|
||||
|
||||
private static <V> Collector<V, ?, Set<V>> toOrderSet() {
|
||||
return Collectors.toCollection(InsertionOrderUtil::newSet);
|
||||
}
|
||||
|
|
@ -792,6 +854,11 @@ public class HugeTask<V> extends FutureTask<V> {
|
|||
public static final String DEPENDENCIES = "~task_dependencies";
|
||||
public static final String SERVER = "~task_server";
|
||||
|
||||
private static final String[] METADATA_PROPERTIES = new String[]{
|
||||
TYPE, NAME, CALLABLE, DESCRIPTION, CONTEXT, STATUS, PROGRESS,
|
||||
CREATE, UPDATE, RETRIES, DEPENDENCIES, INPUT, SERVER
|
||||
};
|
||||
|
||||
//public static final String PARENT = hide("parent");
|
||||
//public static final String CHILDREN = hide("children");
|
||||
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ public class StandardTaskScheduler implements TaskScheduler {
|
|||
String page = this.supportsPaging() ? PageInfo.PAGE_NONE : null;
|
||||
do {
|
||||
Iterator<HugeTask<V>> iter;
|
||||
for (iter = this.findTask(status, PAGE_SIZE, page);
|
||||
for (iter = this.findTask(status, PAGE_SIZE, page, false);
|
||||
iter.hasNext(); ) {
|
||||
HugeTask<V> task = iter.next();
|
||||
if (selfServer.equals(task.server())) {
|
||||
|
|
@ -323,7 +323,8 @@ public class StandardTaskScheduler implements TaskScheduler {
|
|||
Collection<HugeServerInfo> serverInfos = this.serverManager().allServerInfos();
|
||||
String page = this.supportsPaging() ? PageInfo.PAGE_NONE : null;
|
||||
do {
|
||||
Iterator<HugeTask<Object>> tasks = this.tasks(TaskStatus.SCHEDULING, PAGE_SIZE, page);
|
||||
Iterator<HugeTask<Object>> tasks = this.tasks(TaskStatus.SCHEDULING, PAGE_SIZE, page,
|
||||
false);
|
||||
while (tasks.hasNext()) {
|
||||
HugeTask<?> task = tasks.next();
|
||||
if (task.server() != null) {
|
||||
|
|
@ -365,7 +366,8 @@ public class StandardTaskScheduler implements TaskScheduler {
|
|||
protected void executeTasksOnWorker(Id server) {
|
||||
String page = this.supportsPaging() ? PageInfo.PAGE_NONE : null;
|
||||
do {
|
||||
Iterator<HugeTask<Object>> tasks = this.tasks(TaskStatus.SCHEDULED, PAGE_SIZE, page);
|
||||
Iterator<HugeTask<Object>> tasks = this.tasks(TaskStatus.SCHEDULED, PAGE_SIZE, page,
|
||||
false);
|
||||
while (tasks.hasNext()) {
|
||||
HugeTask<?> task = tasks.next();
|
||||
this.initTaskCallable(task);
|
||||
|
|
@ -394,7 +396,8 @@ public class StandardTaskScheduler implements TaskScheduler {
|
|||
protected void cancelTasksOnWorker(Id server) {
|
||||
String page = this.supportsPaging() ? PageInfo.PAGE_NONE : null;
|
||||
do {
|
||||
Iterator<HugeTask<Object>> tasks = this.tasks(TaskStatus.CANCELLING, PAGE_SIZE, page);
|
||||
Iterator<HugeTask<Object>> tasks = this.tasks(TaskStatus.CANCELLING, PAGE_SIZE, page,
|
||||
false);
|
||||
while (tasks.hasNext()) {
|
||||
HugeTask<?> task = tasks.next();
|
||||
Id taskServer = task.server();
|
||||
|
|
@ -494,24 +497,35 @@ public class StandardTaskScheduler implements TaskScheduler {
|
|||
|
||||
@Override
|
||||
public <V> HugeTask<V> task(Id id) {
|
||||
return this.task(id, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> HugeTask<V> task(Id id, boolean withResult) {
|
||||
E.checkArgumentNotNull(id, "Parameter task id can't be null");
|
||||
@SuppressWarnings("unchecked")
|
||||
HugeTask<V> task = (HugeTask<V>) this.tasks.get(id);
|
||||
if (task != null) {
|
||||
return task;
|
||||
return withResult ? task : task.copyWithoutResult();
|
||||
}
|
||||
return this.findTask(id);
|
||||
return this.findTask(id, withResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Iterator<HugeTask<V>> tasks(List<Id> ids) {
|
||||
return this.tasks(ids, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Iterator<HugeTask<V>> tasks(List<Id> ids,
|
||||
boolean withResult) {
|
||||
List<Id> taskIdsNotInMem = new ArrayList<>();
|
||||
List<HugeTask<V>> taskInMem = new ArrayList<>();
|
||||
for (Id id : ids) {
|
||||
@SuppressWarnings("unchecked")
|
||||
HugeTask<V> task = (HugeTask<V>) this.tasks.get(id);
|
||||
if (task != null) {
|
||||
taskInMem.add(task);
|
||||
taskInMem.add(withResult ? task : task.copyWithoutResult());
|
||||
} else {
|
||||
taskIdsNotInMem.add(id);
|
||||
}
|
||||
|
|
@ -522,27 +536,37 @@ public class StandardTaskScheduler implements TaskScheduler {
|
|||
} else {
|
||||
iterator = new ExtendableIterator<>(taskInMem.iterator());
|
||||
}
|
||||
iterator.extend(this.findTasks(taskIdsNotInMem));
|
||||
iterator.extend(this.findTasks(taskIdsNotInMem, withResult));
|
||||
return iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Iterator<HugeTask<V>> tasks(TaskStatus status,
|
||||
long limit, String page) {
|
||||
public <V> Iterator<HugeTask<V>> tasks(TaskStatus status, long limit,
|
||||
String page) {
|
||||
return this.tasks(status, limit, page, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Iterator<HugeTask<V>> tasks(TaskStatus status, long limit,
|
||||
String page, boolean withResult) {
|
||||
if (status == null) {
|
||||
return this.findAllTask(limit, page);
|
||||
return this.findAllTask(limit, page, withResult);
|
||||
}
|
||||
return this.findTask(status, limit, page);
|
||||
return this.findTask(status, limit, page, withResult);
|
||||
}
|
||||
|
||||
public <V> HugeTask<V> findTask(Id id) {
|
||||
return this.findTask(id, true);
|
||||
}
|
||||
|
||||
public <V> HugeTask<V> findTask(Id id, boolean withResult) {
|
||||
HugeTask<V> result = this.call(() -> {
|
||||
Iterator<Vertex> vertices = this.tx().queryTaskInfos(id);
|
||||
Vertex vertex = QueryResults.one(vertices);
|
||||
if (vertex == null) {
|
||||
return null;
|
||||
}
|
||||
return HugeTask.fromVertex(vertex);
|
||||
return HugeTask.fromVertex(vertex, withResult);
|
||||
});
|
||||
if (result == null) {
|
||||
throw new NotFoundException("Can't find task with id '%s'", id);
|
||||
|
|
@ -551,23 +575,40 @@ public class StandardTaskScheduler implements TaskScheduler {
|
|||
}
|
||||
|
||||
public <V> Iterator<HugeTask<V>> findTasks(List<Id> ids) {
|
||||
return this.queryTask(ids);
|
||||
return this.findTasks(ids, true);
|
||||
}
|
||||
|
||||
public <V> Iterator<HugeTask<V>> findTasks(List<Id> ids,
|
||||
boolean withResult) {
|
||||
return this.queryTask(ids, withResult);
|
||||
}
|
||||
|
||||
public <V> Iterator<HugeTask<V>> findAllTask(long limit, String page) {
|
||||
return this.queryTask(ImmutableMap.of(), limit, page);
|
||||
return this.findAllTask(limit, page, true);
|
||||
}
|
||||
|
||||
public <V> Iterator<HugeTask<V>> findAllTask(long limit, String page,
|
||||
boolean withResult) {
|
||||
return this.queryTask(ImmutableMap.of(), limit, page, withResult);
|
||||
}
|
||||
|
||||
public <V> Iterator<HugeTask<V>> findTask(TaskStatus status,
|
||||
long limit, String page) {
|
||||
return this.queryTask(P.STATUS, status.code(), limit, page);
|
||||
return this.findTask(status, limit, page, true);
|
||||
}
|
||||
|
||||
public <V> Iterator<HugeTask<V>> findTask(TaskStatus status,
|
||||
long limit, String page,
|
||||
boolean withResult) {
|
||||
return this.queryTask(P.STATUS, status.code(), limit, page,
|
||||
withResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> HugeTask<V> delete(Id id, boolean force) {
|
||||
this.checkOnMasterNode("delete");
|
||||
|
||||
HugeTask<?> task = this.task(id);
|
||||
HugeTask<?> task = this.task(id, false);
|
||||
/*
|
||||
* The following is out of date when the task running on worker node:
|
||||
* HugeTask<?> task = this.tasks.get(id);
|
||||
|
|
@ -592,11 +633,11 @@ public class StandardTaskScheduler implements TaskScheduler {
|
|||
if (vertex == null) {
|
||||
return null;
|
||||
}
|
||||
HugeTask<V> result = HugeTask.fromVertex(vertex);
|
||||
HugeTask<V> result = HugeTask.fromVertex(vertex, false);
|
||||
E.checkState(force || result.completed(),
|
||||
"Can't delete incomplete task '%s' in status %s",
|
||||
id, result.status());
|
||||
this.tx().removeVertex(vertex);
|
||||
this.tx().removeTaskVertex(vertex);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
|
@ -674,11 +715,24 @@ public class StandardTaskScheduler implements TaskScheduler {
|
|||
|
||||
private <V> Iterator<HugeTask<V>> queryTask(String key, Object value,
|
||||
long limit, String page) {
|
||||
return this.queryTask(ImmutableMap.of(key, value), limit, page);
|
||||
return this.queryTask(key, value, limit, page, true);
|
||||
}
|
||||
|
||||
private <V> Iterator<HugeTask<V>> queryTask(String key, Object value,
|
||||
long limit, String page,
|
||||
boolean withResult) {
|
||||
return this.queryTask(ImmutableMap.of(key, value), limit, page,
|
||||
withResult);
|
||||
}
|
||||
|
||||
private <V> Iterator<HugeTask<V>> queryTask(Map<String, Object> conditions,
|
||||
long limit, String page) {
|
||||
return this.queryTask(conditions, limit, page, true);
|
||||
}
|
||||
|
||||
private <V> Iterator<HugeTask<V>> queryTask(Map<String, Object> conditions,
|
||||
long limit, String page,
|
||||
boolean withResult) {
|
||||
return this.call(() -> {
|
||||
ConditionQuery query;
|
||||
if (this.graph.backendStoreFeatures().supportsTaskAndServerVertex()) {
|
||||
|
|
@ -701,18 +755,27 @@ public class StandardTaskScheduler implements TaskScheduler {
|
|||
}
|
||||
Iterator<Vertex> vertices = this.tx().queryVertices(query);
|
||||
Iterator<HugeTask<V>> tasks =
|
||||
new MapperIterator<>(vertices, HugeTask::fromVertex);
|
||||
new MapperIterator<>(vertices, vertex -> {
|
||||
return HugeTask.fromVertex(vertex, withResult);
|
||||
});
|
||||
// Convert iterator to list to avoid across thread tx accessed
|
||||
return QueryResults.toList(tasks);
|
||||
});
|
||||
}
|
||||
|
||||
private <V> Iterator<HugeTask<V>> queryTask(List<Id> ids) {
|
||||
return this.queryTask(ids, true);
|
||||
}
|
||||
|
||||
private <V> Iterator<HugeTask<V>> queryTask(List<Id> ids,
|
||||
boolean withResult) {
|
||||
return this.call(() -> {
|
||||
Object[] idArray = ids.toArray(new Id[0]);
|
||||
Iterator<Vertex> vertices = this.tx().queryTaskInfos(idArray);
|
||||
Iterator<HugeTask<V>> tasks =
|
||||
new MapperIterator<>(vertices, HugeTask::fromVertex);
|
||||
new MapperIterator<>(vertices, vertex -> {
|
||||
return HugeTask.fromVertex(vertex, withResult);
|
||||
});
|
||||
// Convert iterator to list to avoid across thread tx accessed
|
||||
return QueryResults.toList(tasks);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -104,22 +104,29 @@ public abstract class TaskAndResultScheduler implements TaskScheduler {
|
|||
|
||||
@Override
|
||||
public <V> HugeTask<V> task(Id id) {
|
||||
return this.task(id, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> HugeTask<V> task(Id id, boolean withResult) {
|
||||
HugeTask<V> task = this.call(() -> {
|
||||
Iterator<Vertex> vertices = this.tx().queryTaskInfos(id);
|
||||
Vertex vertex = QueryResults.one(vertices);
|
||||
if (vertex == null) {
|
||||
return null;
|
||||
}
|
||||
return HugeTask.fromVertex(vertex);
|
||||
return HugeTask.fromVertex(vertex, withResult);
|
||||
});
|
||||
|
||||
if (task == null) {
|
||||
throw new NotFoundException("Can't find task with id '%s'", id);
|
||||
}
|
||||
|
||||
HugeTaskResult taskResult = queryTaskResult(id);
|
||||
if (taskResult != null) {
|
||||
task.result(taskResult);
|
||||
if (withResult) {
|
||||
HugeTaskResult taskResult = queryTaskResult(id);
|
||||
if (taskResult != null) {
|
||||
task.result(taskResult);
|
||||
}
|
||||
}
|
||||
|
||||
return task;
|
||||
|
|
@ -127,17 +134,39 @@ public abstract class TaskAndResultScheduler implements TaskScheduler {
|
|||
|
||||
@Override
|
||||
public <V> Iterator<HugeTask<V>> tasks(List<Id> ids) {
|
||||
return this.tasksWithoutResult(ids);
|
||||
return this.tasks(ids, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Iterator<HugeTask<V>> tasks(List<Id> ids,
|
||||
boolean withResult) {
|
||||
if (!withResult) {
|
||||
return this.tasksWithoutResult(ids);
|
||||
}
|
||||
return this.queryTask(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Iterator<HugeTask<V>> tasks(TaskStatus status, long limit,
|
||||
String page) {
|
||||
if (status == null) {
|
||||
return this.queryTaskWithoutResult(ImmutableMap.of(), limit, page);
|
||||
return this.tasks(status, limit, page, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Iterator<HugeTask<V>> tasks(TaskStatus status, long limit,
|
||||
String page, boolean withResult) {
|
||||
if (!withResult) {
|
||||
if (status == null) {
|
||||
return this.queryTaskWithoutResult(ImmutableMap.of(), limit,
|
||||
page);
|
||||
}
|
||||
return this.queryTaskWithoutResult(HugeTask.P.STATUS,
|
||||
status.code(), limit, page);
|
||||
}
|
||||
return this.queryTaskWithoutResult(HugeTask.P.STATUS, status.code(),
|
||||
limit, page);
|
||||
if (status == null) {
|
||||
return this.queryTask(ImmutableMap.of(), limit, page);
|
||||
}
|
||||
return this.queryTask(HugeTask.P.STATUS, status.code(), limit, page);
|
||||
}
|
||||
|
||||
protected <V> Iterator<HugeTask<V>> queryTask(String key, Object value,
|
||||
|
|
@ -216,7 +245,7 @@ public abstract class TaskAndResultScheduler implements TaskScheduler {
|
|||
if (vertex == null) {
|
||||
return null;
|
||||
}
|
||||
return HugeTask.fromVertex(vertex);
|
||||
return HugeTask.fromVertex(vertex, false);
|
||||
});
|
||||
|
||||
return result;
|
||||
|
|
@ -227,7 +256,9 @@ public abstract class TaskAndResultScheduler implements TaskScheduler {
|
|||
Object[] idArray = ids.toArray(new Id[ids.size()]);
|
||||
Iterator<Vertex> vertices = this.tx().queryTaskInfos(idArray);
|
||||
Iterator<HugeTask<V>> tasks =
|
||||
new MapperIterator<>(vertices, HugeTask::fromVertex);
|
||||
new MapperIterator<>(vertices, vertex -> {
|
||||
return HugeTask.fromVertex(vertex, false);
|
||||
});
|
||||
// Convert iterator to list to avoid across thread tx accessed
|
||||
return QueryResults.toList(tasks);
|
||||
});
|
||||
|
|
@ -268,12 +299,23 @@ public abstract class TaskAndResultScheduler implements TaskScheduler {
|
|||
}
|
||||
Iterator<Vertex> vertices = this.tx().queryTaskInfos(query);
|
||||
Iterator<HugeTask<V>> tasks =
|
||||
new MapperIterator<>(vertices, HugeTask::fromVertex);
|
||||
new MapperIterator<>(vertices, vertex -> {
|
||||
return HugeTask.fromVertex(vertex, false);
|
||||
});
|
||||
// Convert iterator to list to avoid across thread tx accessed
|
||||
return QueryResults.toList(tasks);
|
||||
});
|
||||
}
|
||||
|
||||
protected void deleteTaskResultFromTx(Id taskId) {
|
||||
Iterator<Vertex> vertices =
|
||||
this.tx().queryTaskInfos(HugeTaskResult.genId(taskId));
|
||||
HugeVertex vertex = (HugeVertex) QueryResults.one(vertices);
|
||||
if (vertex != null) {
|
||||
this.tx().removeTaskVertex(vertex);
|
||||
}
|
||||
}
|
||||
|
||||
protected HugeTaskResult queryTaskResult(Id taskid) {
|
||||
HugeTaskResult result = this.call(() -> {
|
||||
Iterator<Vertex> vertices =
|
||||
|
|
|
|||
|
|
@ -49,11 +49,25 @@ public interface TaskScheduler {
|
|||
|
||||
<V> HugeTask<V> task(Id id);
|
||||
|
||||
default <V> HugeTask<V> task(Id id, boolean withResult) {
|
||||
return this.task(id);
|
||||
}
|
||||
|
||||
<V> Iterator<HugeTask<V>> tasks(List<Id> ids);
|
||||
|
||||
default <V> Iterator<HugeTask<V>> tasks(List<Id> ids,
|
||||
boolean withResult) {
|
||||
return this.tasks(ids);
|
||||
}
|
||||
|
||||
<V> Iterator<HugeTask<V>> tasks(TaskStatus status,
|
||||
long limit, String page);
|
||||
|
||||
default <V> Iterator<HugeTask<V>> tasks(TaskStatus status, long limit,
|
||||
String page, boolean withResult) {
|
||||
return this.tasks(status, limit, page);
|
||||
}
|
||||
|
||||
void init();
|
||||
|
||||
boolean close();
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import org.apache.hugegraph.schema.IndexLabel;
|
|||
import org.apache.hugegraph.schema.PropertyKey;
|
||||
import org.apache.hugegraph.schema.SchemaManager;
|
||||
import org.apache.hugegraph.schema.VertexLabel;
|
||||
import org.apache.hugegraph.structure.HugeIndex;
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.type.HugeType;
|
||||
import org.apache.hugegraph.type.define.Cardinality;
|
||||
|
|
@ -74,6 +75,34 @@ public class TaskTransaction extends GraphTransaction {
|
|||
return false;
|
||||
}
|
||||
|
||||
public void removeTaskVertex(HugeVertex vertex) {
|
||||
this.checkOwnerThread();
|
||||
|
||||
this.beforeWrite();
|
||||
|
||||
this.doRemove(this.serializer.writeVertex(vertex.prepareRemoved()));
|
||||
if (TASK.equals(vertex.schemaLabel().name())) {
|
||||
this.updateIndex(this.indexLabel(HugeTask.P.STATUS).id(),
|
||||
vertex, true);
|
||||
}
|
||||
this.removeLabelIndex(vertex);
|
||||
|
||||
this.afterWrite();
|
||||
}
|
||||
|
||||
private void removeLabelIndex(HugeVertex vertex) {
|
||||
if (this.store().features().supportsQueryByLabel() ||
|
||||
!vertex.schemaLabel().enableLabelIndex()) {
|
||||
return;
|
||||
}
|
||||
|
||||
HugeIndex index = new HugeIndex(this.graph(),
|
||||
IndexLabel.label(vertex.type()));
|
||||
index.fieldValues(vertex.schemaLabel().id());
|
||||
index.elementIds(vertex.id(), vertex.expiredTime());
|
||||
this.doEliminate(this.serializer.writeIndex(index));
|
||||
}
|
||||
|
||||
public void initSchema() {
|
||||
if (this.existVertexLabel(TASK)) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -87,6 +87,28 @@ public class TaskApiTest extends BaseApiTest {
|
|||
Assert.assertEquals("success", status);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetWithoutResult() {
|
||||
int taskId = this.gremlinJob("1 + 2");
|
||||
|
||||
waitTaskSuccess(taskId);
|
||||
|
||||
Response r = client().get(PATH, ImmutableMap.of("limit", -1));
|
||||
String content = assertResponseStatus(200, r);
|
||||
Assert.assertFalse(content, content.contains("task_result"));
|
||||
|
||||
r = client().get(PATH, String.valueOf(taskId));
|
||||
content = assertResponseStatus(200, r);
|
||||
assertJsonContains(content, "task_result");
|
||||
|
||||
r = client().get(PATH + taskId,
|
||||
ImmutableMap.of("with_result", false));
|
||||
content = assertResponseStatus(200, r);
|
||||
assertJsonContains(content, "id");
|
||||
assertJsonContains(content, "task_callable");
|
||||
Assert.assertFalse(content, content.contains("task_result"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancel() {
|
||||
// create a task
|
||||
|
|
@ -143,8 +165,12 @@ public class TaskApiTest extends BaseApiTest {
|
|||
}
|
||||
|
||||
private int gremlinJob() {
|
||||
return this.gremlinJob("Thread.sleep(1000L)");
|
||||
}
|
||||
|
||||
private int gremlinJob(String gremlin) {
|
||||
String body = "{" +
|
||||
"\"gremlin\":\"Thread.sleep(1000L)\"," +
|
||||
"\"gremlin\":\"" + gremlin + "\"," +
|
||||
"\"bindings\":{}," +
|
||||
"\"language\":\"gremlin-groovy\"," +
|
||||
"\"aliases\":{}}";
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import org.apache.hugegraph.dist.RegisterUtil;
|
|||
import org.apache.hugegraph.masterelection.GlobalMasterInfo;
|
||||
import org.apache.hugegraph.meta.MetaManager;
|
||||
import org.apache.hugegraph.meta.PdMetaDriver;
|
||||
import org.apache.hugegraph.task.TaskAndResultSchedulerTest;
|
||||
import org.apache.hugegraph.testutil.Utils;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.junit.AfterClass;
|
||||
|
|
@ -45,6 +46,7 @@ import org.slf4j.Logger;
|
|||
PropertyCoreTest.EdgePropertyCoreTest.class,
|
||||
RestoreCoreTest.class,
|
||||
TaskCoreTest.class,
|
||||
TaskAndResultSchedulerTest.class,
|
||||
AuthTest.class,
|
||||
MultiGraphsTest.class,
|
||||
RamTableTest.class
|
||||
|
|
|
|||
|
|
@ -115,6 +115,88 @@ public class TaskCoreTest extends BaseCoreTest {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTaskWithoutResult() throws TimeoutException {
|
||||
HugeGraph graph = graph();
|
||||
TaskScheduler scheduler = graph.taskScheduler();
|
||||
|
||||
Id id = IdGenerator.of(88889);
|
||||
HugeTask<?> task = new HugeTask<>(id, null, new SleepCallable<>());
|
||||
task.type("test");
|
||||
task.name("metadata-task-in-memory");
|
||||
scheduler.schedule(task);
|
||||
|
||||
try {
|
||||
Whitebox.setInternalState(task, "result", "\"in-memory-result\"");
|
||||
|
||||
HugeTask<?> taskWithoutResult = scheduler.task(id, false);
|
||||
Assert.assertEquals("metadata-task-in-memory",
|
||||
taskWithoutResult.name());
|
||||
Assert.assertNull(taskWithoutResult.result());
|
||||
|
||||
Iterator<HugeTask<Object>> iter = scheduler.tasks(ImmutableList.of(id),
|
||||
false);
|
||||
Assert.assertTrue(iter.hasNext());
|
||||
taskWithoutResult = iter.next();
|
||||
Assert.assertEquals("metadata-task-in-memory",
|
||||
taskWithoutResult.name());
|
||||
Assert.assertNull(taskWithoutResult.result());
|
||||
Assert.assertFalse(iter.hasNext());
|
||||
} finally {
|
||||
Whitebox.setInternalState(task, "result", null);
|
||||
}
|
||||
|
||||
scheduler.waitUntilTaskCompleted(id, 10);
|
||||
scheduler.delete(id, false);
|
||||
|
||||
id = IdGenerator.of(88890);
|
||||
task = new HugeTask<>(id, null, new MetadataResultCallable());
|
||||
task.type("test");
|
||||
task.name("metadata-task");
|
||||
scheduler.schedule(task);
|
||||
|
||||
scheduler.waitUntilTaskCompleted(id, 10);
|
||||
|
||||
HugeTask<?> taskWithResult = scheduler.task(id, true);
|
||||
Assert.assertEquals("\"metadata-result\"", taskWithResult.result());
|
||||
|
||||
HugeTask<?> taskWithoutResult = scheduler.task(id, false);
|
||||
Assert.assertEquals("metadata-task", taskWithoutResult.name());
|
||||
Assert.assertNull(taskWithoutResult.result());
|
||||
|
||||
Iterator<HugeTask<Object>> iter = scheduler.tasks(ImmutableList.of(id));
|
||||
Assert.assertTrue(iter.hasNext());
|
||||
taskWithResult = iter.next();
|
||||
Assert.assertEquals("metadata-task", taskWithResult.name());
|
||||
Assert.assertEquals("\"metadata-result\"", taskWithResult.result());
|
||||
Assert.assertFalse(iter.hasNext());
|
||||
|
||||
iter = scheduler.tasks(ImmutableList.of(id), false);
|
||||
Assert.assertTrue(iter.hasNext());
|
||||
taskWithoutResult = iter.next();
|
||||
Assert.assertEquals("metadata-task", taskWithoutResult.name());
|
||||
Assert.assertNull(taskWithoutResult.result());
|
||||
Assert.assertFalse(iter.hasNext());
|
||||
|
||||
iter = scheduler.tasks(TaskStatus.SUCCESS, 10, null, false);
|
||||
Assert.assertTrue(iter.hasNext());
|
||||
taskWithoutResult = iter.next();
|
||||
Assert.assertEquals("metadata-task", taskWithoutResult.name());
|
||||
Assert.assertNull(taskWithoutResult.result());
|
||||
|
||||
iter = scheduler.tasks(TaskStatus.SUCCESS, 10, null);
|
||||
Assert.assertTrue(iter.hasNext());
|
||||
taskWithResult = iter.next();
|
||||
Assert.assertEquals("metadata-task", taskWithResult.name());
|
||||
Assert.assertEquals("\"metadata-result\"", taskWithResult.result());
|
||||
|
||||
Id taskId = id;
|
||||
scheduler.delete(taskId, false);
|
||||
Assert.assertThrows(NotFoundException.class, () -> {
|
||||
scheduler.task(taskId);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTaskWithFailure() throws TimeoutException {
|
||||
HugeGraph graph = graph();
|
||||
|
|
@ -714,4 +796,21 @@ public class TaskCoreTest extends BaseCoreTest {
|
|||
this.graph().taskScheduler().save(this.task());
|
||||
}
|
||||
}
|
||||
|
||||
public static class MetadataResultCallable extends TaskCallable<String> {
|
||||
|
||||
public MetadataResultCallable() {
|
||||
// pass
|
||||
}
|
||||
|
||||
@Override
|
||||
public String call() {
|
||||
return "metadata-result";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void done() {
|
||||
this.graph().taskScheduler().save(this.task());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.task;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.hugegraph.HugeException;
|
||||
import org.apache.hugegraph.HugeGraphParams;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.id.IdGenerator;
|
||||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.core.BaseCoreTest;
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.testutil.Assert;
|
||||
import org.apache.hugegraph.testutil.Whitebox;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class TaskAndResultSchedulerTest extends BaseCoreTest {
|
||||
|
||||
@Test
|
||||
public void testMetadataOnlyReadsSkipLargeCompressedTaskResult() {
|
||||
Id id = IdGenerator.of(88901);
|
||||
TestDistributedTaskScheduler scheduler = this.newScheduler();
|
||||
|
||||
try {
|
||||
scheduler.init();
|
||||
|
||||
String largeResult = largeTaskResult();
|
||||
HugeTask<Object> task = newTask(id, "large-result-task",
|
||||
TaskStatus.SUCCESS, largeResult);
|
||||
scheduler.save(task);
|
||||
Assert.assertTrue(scheduler.taskResultVertexExists(id));
|
||||
|
||||
HugeTask<?> taskWithResult = scheduler.task(id, true);
|
||||
Assert.assertEquals(largeResult, taskWithResult.result());
|
||||
Assert.assertGt(0, scheduler.resultReadCount());
|
||||
|
||||
scheduler.resetResultReadCount();
|
||||
scheduler.forbidResultRead(true);
|
||||
|
||||
HugeTask<?> taskWithoutResult = scheduler.task(id, false);
|
||||
assertTaskWithoutResult(taskWithoutResult, id);
|
||||
Assert.assertEquals(0, scheduler.resultReadCount());
|
||||
|
||||
Iterator<HugeTask<Object>> tasks = scheduler.tasks(ImmutableList.of(id),
|
||||
false);
|
||||
assertIteratorContainsTaskWithoutResult(tasks, id);
|
||||
Assert.assertEquals(0, scheduler.resultReadCount());
|
||||
|
||||
tasks = scheduler.tasks(TaskStatus.SUCCESS, 10L, null, false);
|
||||
assertIteratorContainsTaskWithoutResult(tasks, id);
|
||||
Assert.assertEquals(0, scheduler.resultReadCount());
|
||||
|
||||
tasks = scheduler.tasks(ImmutableList.of(id));
|
||||
assertIteratorContainsTaskWithoutResult(tasks, id);
|
||||
Assert.assertEquals(0, scheduler.resultReadCount());
|
||||
|
||||
tasks = scheduler.tasks(TaskStatus.SUCCESS, 10L, null);
|
||||
assertIteratorContainsTaskWithoutResult(tasks, id);
|
||||
Assert.assertEquals(0, scheduler.resultReadCount());
|
||||
|
||||
tasks = scheduler.queryMetadataTasksByStatus(TaskStatus.SUCCESS);
|
||||
assertIteratorContainsTaskWithoutResult(tasks, id);
|
||||
Assert.assertEquals(0, scheduler.resultReadCount());
|
||||
|
||||
scheduler.forbidResultRead(false);
|
||||
scheduler.resetResultReadCount();
|
||||
|
||||
tasks = scheduler.tasks(ImmutableList.of(id), true);
|
||||
assertIteratorContainsTaskWithResult(tasks, id, largeResult);
|
||||
Assert.assertGt(0, scheduler.resultReadCount());
|
||||
} finally {
|
||||
scheduler.forbidResultRead(false);
|
||||
scheduler.deleteFromDBForTest(id);
|
||||
scheduler.closeAndShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDistributedDeleteKeepsTaskResultRecoverable() {
|
||||
Id id = IdGenerator.of(88902);
|
||||
TestDistributedTaskScheduler scheduler = this.newScheduler();
|
||||
|
||||
try {
|
||||
scheduler.init();
|
||||
|
||||
HugeTask<Object> task = newTask(id, "delete-order-task",
|
||||
TaskStatus.SUCCESS, "\"result\"");
|
||||
scheduler.save(task);
|
||||
Assert.assertTrue(scheduler.taskVertexExists(id));
|
||||
Assert.assertTrue(scheduler.taskResultVertexExists(id));
|
||||
|
||||
scheduler.failNextTaskResultDelete();
|
||||
Assert.assertThrows(HugeException.class, () -> {
|
||||
scheduler.delete(id, true);
|
||||
});
|
||||
|
||||
Assert.assertTrue(scheduler.taskVertexExists(id));
|
||||
Assert.assertEquals(TaskStatus.DELETING, scheduler.taskStatus(id));
|
||||
Assert.assertTrue(scheduler.taskResultVertexExists(id));
|
||||
|
||||
scheduler.cronSchedule();
|
||||
Assert.assertFalse(scheduler.taskVertexExists(id));
|
||||
Assert.assertFalse(scheduler.taskResultVertexExists(id));
|
||||
} finally {
|
||||
scheduler.allowTaskResultDelete();
|
||||
scheduler.deleteFromDBForTest(id);
|
||||
scheduler.closeAndShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private TestDistributedTaskScheduler newScheduler() {
|
||||
return new TestDistributedTaskScheduler(this.params());
|
||||
}
|
||||
|
||||
private static HugeTask<Object> newTask(Id id, String name,
|
||||
TaskStatus status, String result) {
|
||||
HugeTask<Object> task = new HugeTask<>(id, null, new EmptyCallable());
|
||||
task.type("test");
|
||||
task.name(name);
|
||||
task.overwriteStatus(status);
|
||||
Whitebox.setInternalState(task, "result", result);
|
||||
return task;
|
||||
}
|
||||
|
||||
private static void assertTaskWithoutResult(HugeTask<?> task, Id id) {
|
||||
Assert.assertEquals(id, task.id());
|
||||
Assert.assertNull(task.result());
|
||||
}
|
||||
|
||||
private static void assertIteratorContainsTaskWithoutResult(
|
||||
Iterator<HugeTask<Object>> tasks, Id id) {
|
||||
boolean matched = false;
|
||||
while (tasks.hasNext()) {
|
||||
HugeTask<?> task = tasks.next();
|
||||
if (id.equals(task.id())) {
|
||||
assertTaskWithoutResult(task, id);
|
||||
matched = true;
|
||||
}
|
||||
}
|
||||
Assert.assertTrue(matched);
|
||||
}
|
||||
|
||||
private static void assertIteratorContainsTaskWithResult(
|
||||
Iterator<HugeTask<Object>> tasks, Id id, String result) {
|
||||
boolean matched = false;
|
||||
while (tasks.hasNext()) {
|
||||
HugeTask<?> task = tasks.next();
|
||||
if (id.equals(task.id())) {
|
||||
Assert.assertEquals(id, task.id());
|
||||
Assert.assertEquals(result, task.result());
|
||||
matched = true;
|
||||
}
|
||||
}
|
||||
Assert.assertTrue(matched);
|
||||
}
|
||||
|
||||
private static String largeTaskResult() {
|
||||
char[] chars = new char[1024 * 1024 + 257];
|
||||
int value = 0x12345678;
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
value = value * 1103515245 + 12345;
|
||||
chars[i] = (char) ('a' + ((value >>> 16) & 0x0F));
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
public static class EmptyCallable extends TaskCallable<Object> {
|
||||
|
||||
public EmptyCallable() {
|
||||
// pass
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object call() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestDistributedTaskScheduler
|
||||
extends DistributedTaskScheduler {
|
||||
|
||||
private final ScheduledThreadPoolExecutor schedulerExecutor;
|
||||
private final ExecutorService taskDbExecutor;
|
||||
private final ExecutorService schemaTaskExecutor;
|
||||
private final ExecutorService olapTaskExecutor;
|
||||
private final ExecutorService gremlinTaskExecutor;
|
||||
private final ExecutorService ephemeralTaskExecutor;
|
||||
private final ExecutorService serverInfoDbExecutor;
|
||||
private final AtomicInteger resultReadCount;
|
||||
private volatile boolean forbidResultRead;
|
||||
private volatile boolean failNextTaskResultDelete;
|
||||
|
||||
TestDistributedTaskScheduler(HugeGraphParams graph) {
|
||||
this(graph, new ScheduledThreadPoolExecutor(1),
|
||||
Executors.newSingleThreadExecutor(),
|
||||
Executors.newSingleThreadExecutor(),
|
||||
Executors.newSingleThreadExecutor(),
|
||||
Executors.newSingleThreadExecutor(),
|
||||
Executors.newSingleThreadExecutor(),
|
||||
Executors.newSingleThreadExecutor());
|
||||
}
|
||||
|
||||
private TestDistributedTaskScheduler(
|
||||
HugeGraphParams graph,
|
||||
ScheduledThreadPoolExecutor schedulerExecutor,
|
||||
ExecutorService taskDbExecutor,
|
||||
ExecutorService schemaTaskExecutor,
|
||||
ExecutorService olapTaskExecutor,
|
||||
ExecutorService gremlinTaskExecutor,
|
||||
ExecutorService ephemeralTaskExecutor,
|
||||
ExecutorService serverInfoDbExecutor) {
|
||||
super(graph, schedulerExecutor, taskDbExecutor, schemaTaskExecutor,
|
||||
olapTaskExecutor, gremlinTaskExecutor, ephemeralTaskExecutor,
|
||||
serverInfoDbExecutor);
|
||||
this.schedulerExecutor = schedulerExecutor;
|
||||
this.taskDbExecutor = taskDbExecutor;
|
||||
this.schemaTaskExecutor = schemaTaskExecutor;
|
||||
this.olapTaskExecutor = olapTaskExecutor;
|
||||
this.gremlinTaskExecutor = gremlinTaskExecutor;
|
||||
this.ephemeralTaskExecutor = ephemeralTaskExecutor;
|
||||
this.serverInfoDbExecutor = serverInfoDbExecutor;
|
||||
this.resultReadCount = new AtomicInteger();
|
||||
this.forbidResultRead = false;
|
||||
this.failNextTaskResultDelete = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HugeTaskResult queryTaskResult(Id taskid) {
|
||||
this.resultReadCount.incrementAndGet();
|
||||
this.checkResultReadAllowed();
|
||||
return super.queryTaskResult(taskid);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterator<HugeTaskResult> queryTaskResult(List<Id> taskIds) {
|
||||
this.resultReadCount.incrementAndGet();
|
||||
this.checkResultReadAllowed();
|
||||
return super.queryTaskResult(taskIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteTaskResultFromTx(Id taskId) {
|
||||
if (this.failNextTaskResultDelete) {
|
||||
this.failNextTaskResultDelete = false;
|
||||
throw new HugeException("Mock task result delete failure");
|
||||
}
|
||||
super.deleteTaskResultFromTx(taskId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isLockedTask(String taskId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public <V> HugeTask<V> deleteFromDBForTest(Id id) {
|
||||
return super.deleteFromDB(id);
|
||||
}
|
||||
|
||||
public Iterator<HugeTask<Object>> queryMetadataTasksByStatus(
|
||||
TaskStatus status) {
|
||||
return super.queryTaskWithoutResultByStatus(status);
|
||||
}
|
||||
|
||||
public boolean taskVertexExists(Id id) {
|
||||
return this.vertexExists(id);
|
||||
}
|
||||
|
||||
public boolean taskResultVertexExists(Id id) {
|
||||
return this.vertexExists(HugeTaskResult.genId(id));
|
||||
}
|
||||
|
||||
public TaskStatus taskStatus(Id id) {
|
||||
return this.task(id, false).status();
|
||||
}
|
||||
|
||||
public int resultReadCount() {
|
||||
return this.resultReadCount.get();
|
||||
}
|
||||
|
||||
public void resetResultReadCount() {
|
||||
this.resultReadCount.set(0);
|
||||
}
|
||||
|
||||
public void forbidResultRead(boolean forbid) {
|
||||
this.forbidResultRead = forbid;
|
||||
}
|
||||
|
||||
public void failNextTaskResultDelete() {
|
||||
this.failNextTaskResultDelete = true;
|
||||
}
|
||||
|
||||
public void allowTaskResultDelete() {
|
||||
this.failNextTaskResultDelete = false;
|
||||
}
|
||||
|
||||
public void closeAndShutdown() {
|
||||
try {
|
||||
this.close();
|
||||
} finally {
|
||||
this.schedulerExecutor.shutdownNow();
|
||||
this.taskDbExecutor.shutdownNow();
|
||||
this.schemaTaskExecutor.shutdownNow();
|
||||
this.olapTaskExecutor.shutdownNow();
|
||||
this.gremlinTaskExecutor.shutdownNow();
|
||||
this.ephemeralTaskExecutor.shutdownNow();
|
||||
this.serverInfoDbExecutor.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean vertexExists(Object id) {
|
||||
return this.call(() -> {
|
||||
Iterator<Vertex> vertices = this.tx().queryTaskInfos(id);
|
||||
HugeVertex vertex = (HugeVertex) QueryResults.one(vertices);
|
||||
return vertex != null;
|
||||
});
|
||||
}
|
||||
|
||||
private void checkResultReadAllowed() {
|
||||
if (this.forbidResultRead) {
|
||||
throw new AssertionError("Unexpected task result read");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue