This is an automated email from the ASF dual-hosted git repository.

imbajin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hugegraph.git


The following commit(s) were added to refs/heads/master by this push:
     new 63c97ff93 fix(server): avoid loading huge task results for metadata 
queries (#3060)
63c97ff93 is described below

commit 63c97ff93ff83ae005cad0654f961b250aab3cbc
Author: contrueCT <[email protected]>
AuthorDate: Sun Jun 21 20:27:45 2026 +0800

    fix(server): avoid loading huge task results for metadata queries (#3060)
---
 .../java/org/apache/hugegraph/api/job/TaskAPI.java |  16 +-
 .../apache/hugegraph/auth/HugeGraphAuthProxy.java  |  30 +-
 .../hugegraph/task/DistributedTaskScheduler.java   |  18 +-
 .../java/org/apache/hugegraph/task/HugeTask.java   |  69 +++-
 .../hugegraph/task/StandardTaskScheduler.java      | 107 +++++--
 .../hugegraph/task/TaskAndResultScheduler.java     |  64 +++-
 .../org/apache/hugegraph/task/TaskScheduler.java   |  14 +
 .../org/apache/hugegraph/task/TaskTransaction.java |  29 ++
 .../java/org/apache/hugegraph/api/TaskApiTest.java |  28 +-
 .../org/apache/hugegraph/core/CoreTestSuite.java   |   2 +
 .../org/apache/hugegraph/core/TaskCoreTest.java    |  99 ++++++
 .../hugegraph/task/TaskAndResultSchedulerTest.java | 348 +++++++++++++++++++++
 12 files changed, 777 insertions(+), 47 deletions(-)

diff --git 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/TaskAPI.java
 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/TaskAPI.java
index d35cc9a95..7143efb5f 100644
--- 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/TaskAPI.java
+++ 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/TaskAPI.java
@@ -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
diff --git 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java
 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java
index cf390b886..5b117faae 100644
--- 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java
+++ 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java
@@ -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);
         }
 
diff --git 
a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/DistributedTaskScheduler.java
 
b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/DistributedTaskScheduler.java
index b4bba2ea1..3929f8414 100644
--- 
a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/DistributedTaskScheduler.java
+++ 
b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/DistributedTaskScheduler.java
@@ -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;
diff --git 
a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/HugeTask.java
 
b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/HugeTask.java
index f9e4f120f..d5d703928 100644
--- 
a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/HugeTask.java
+++ 
b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/HugeTask.java
@@ -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");
 
diff --git 
a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/StandardTaskScheduler.java
 
b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/StandardTaskScheduler.java
index 5f60792af..1438249a4 100644
--- 
a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/StandardTaskScheduler.java
+++ 
b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/StandardTaskScheduler.java
@@ -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);
         });
diff --git 
a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/TaskAndResultScheduler.java
 
b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/TaskAndResultScheduler.java
index 2ba3fd8a6..26a520bde 100644
--- 
a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/TaskAndResultScheduler.java
+++ 
b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/TaskAndResultScheduler.java
@@ -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) {
+        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);
+        }
         if (status == null) {
-            return this.queryTaskWithoutResult(ImmutableMap.of(), limit, page);
+            return this.queryTask(ImmutableMap.of(), limit, page);
         }
-        return this.queryTaskWithoutResult(HugeTask.P.STATUS, status.code(),
-                                           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 =
diff --git 
a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/TaskScheduler.java
 
b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/TaskScheduler.java
index af789c523..ea10cd006 100644
--- 
a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/TaskScheduler.java
+++ 
b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/TaskScheduler.java
@@ -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();
diff --git 
a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/TaskTransaction.java
 
b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/TaskTransaction.java
index 2b27019c7..9dd4a46aa 100644
--- 
a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/TaskTransaction.java
+++ 
b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/TaskTransaction.java
@@ -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;
diff --git 
a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/api/TaskApiTest.java
 
b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/api/TaskApiTest.java
index 9ed25fd71..f65f651b1 100644
--- 
a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/api/TaskApiTest.java
+++ 
b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/api/TaskApiTest.java
@@ -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\":{}}";
diff --git 
a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/CoreTestSuite.java
 
b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/CoreTestSuite.java
index e78aff7e2..230b8d2d0 100644
--- 
a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/CoreTestSuite.java
+++ 
b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/CoreTestSuite.java
@@ -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
diff --git 
a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/TaskCoreTest.java
 
b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/TaskCoreTest.java
index 212ccc058..2d106fc3b 100644
--- 
a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/TaskCoreTest.java
+++ 
b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/TaskCoreTest.java
@@ -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());
+        }
+    }
 }
diff --git 
a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/task/TaskAndResultSchedulerTest.java
 
b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/task/TaskAndResultSchedulerTest.java
new file mode 100644
index 000000000..15fabf130
--- /dev/null
+++ 
b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/task/TaskAndResultSchedulerTest.java
@@ -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");
+            }
+        }
+    }
+}

Reply via email to