imbajin commented on code in PR #3308:
URL: https://github.com/apache/hugegraph/pull/3308#discussion_r4250613665


##########
hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/core/GraphManager.java:
##########
@@ -2268,40 +2266,77 @@ public void dropGraph(String graphSpace, String name, 
boolean clear) {
         }
 
         String graphName = spaceGraphName(graphSpace, name);
+        boolean alreadyClosed = g.closed();
+        E.checkState(!clear || !alreadyClosed || 
this.removingGraphs.contains(graphName),
+                     "Can't clear an already closed graph '%s' without an 
accepted deletion", graphName);
+        // Task results still need the backend until their scheduler has 
drained.
+        TaskScheduler scheduler = null;
+        if (clear && !alreadyClosed) {
+            try {
+                // Keep the auth proxy's cached owner and its pre-close ADMIN 
check.
+                scheduler = g.taskScheduler();
+            } catch (IllegalStateException e) {
+                if (!(g instanceof StandardHugeGraph) ||
+                    TaskManager.instance().getScheduler(g) != null) {
+                    throw e;
+                }
+                // A previous plain-graph close removed its scheduler but 
retained owners.
+            }
+        }
+        if (clear && scheduler != null && !scheduler.close()) {
+            throw new HugeException("Can't drop graph '%s' while tasks are 
active, " +
+                                    "please retry later", graphName);
+        }
         if (clear) {
-            this.removingGraphs.add(graphName);
             try {
-                this.metaManager.removeGraphConfig(graphSpace, name);
+                if (!this.removingGraphs.contains(graphName)) {
+                    this.metaManager.removeGraphConfig(graphSpace, name);
+                    this.removingGraphs.add(graphName);
+                }
                 this.metaManager.notifyGraphRemove(graphSpace, name);
             } catch (Exception e) {
                 throw new HugeException(
-                        "Failed to remove graph config of '%s'", name, e);
+                        "Failed to remove graph config of '%s'", e, name);
             }
 
-            /**
-             * close task scheduler before clear data,
-             * because taskinfo stored in backend in
-             * {@link org.apache.hugegraph.task.DistributedTaskScheduler}
-             */
+            if (!alreadyClosed) {
+                // Unwrapping an auth proxy preserves its ADMIN permission 
check.
+                HugeGraph underlying = g.hugegraph();
+                if (underlying instanceof StandardHugeGraph) {
+                    ((StandardHugeGraph) underlying).clearBackendForDrop();
+                } else {
+                    g.clearBackend();
+                }
+            }
             try {
-                g.taskScheduler().close();
-            } catch (Throwable t) {
-                LOG.warn(String.format(
-                                 "Error when close TaskScheduler of %s",
-                                 graphName),
-                         t);
+                if (!alreadyClosed) {
+                    g.close();
+                }
+            } catch (Exception e) {
+                if (!g.closed()) {
+                    throw new HugeException("Failed to close graph '%s', 
please retry later",
+                                            e, graphName);
+                }
+                LOG.warn("Graph '{}' closed with an owner cleanup failure", 
graphName, e);
             }
-
-            g.clearBackend();
+        } else {
+            // Remote notifications detach owners only after close succeeds.
             try {
                 g.close();
             } catch (Exception e) {
-                LOG.warn("Failed to close graph", e);
+                if (!g.closed()) {
+                    throw new HugeException("Failed to close graph '%s', 
please retry later",

Review Comment:
   The earlier reload recovery fix has been intentionally removed to keep this 
PR focused on local drain and deletion. The close failure remains visible and 
an open graph remains registered; full reload recovery is deferred rather than 
claimed fixed by this version. The runtime contract now requires restart for 
configuration changes, and the broader website documentation is held as a draft 
pending reconciliation.



##########
hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/task/DistributedTaskScheduler.java:
##########
@@ -469,17 +550,200 @@ private void saveTaskWithoutResult(HugeTask<?> task) {
         });
     }
 
+    private boolean cancelLocal(PendingTask pending, Consumer<Boolean> 
afterCancel) {
+        return this.cancelLocal(pending, null, afterCancel);
+    }
+
+    private boolean cancelLocal(PendingTask pending, Runnable beforeCancel,
+                                Consumer<Boolean> afterCancel) {
+        return this.cancelLocal(pending, beforeCancel, afterCancel, false);
+    }
+
+    private boolean cancelLocal(PendingTask pending, Runnable beforeCancel,
+                                Consumer<Boolean> afterCancel, boolean 
terminalOnly) {
+        if (SCHEDULER_WORKER.get() == this.schedulerExecutor) {
+            return this.cancelOwned(pending, beforeCancel, afterCancel, 
terminalOnly);
+        }
+        // Keep callbacks and final persistence off the request/close thread 
and
+        // the DB worker, because callbacks may synchronously save to that 
worker.
+        return this.call(() -> {
+            SCHEDULER_WORKER.set(this.schedulerExecutor);
+            try {
+                return this.cancelOwned(pending, beforeCancel, afterCancel, 
terminalOnly);
+            } finally {
+                SCHEDULER_WORKER.remove();
+                // An interrupted request may stop waiting before this owner 
finishes.
+                this.finishCancelledQueued(pending);
+            }
+        }, this.schedulerExecutor, true);
+    }
+
+    private boolean cancelOwned(PendingTask pending, Runnable beforeCancel,
+                                Consumer<Boolean> afterCancel, boolean 
terminalOnly) {
+        LockResult acquired = null;
+        boolean acquire;
+        synchronized (pending) {
+            if (this.runningTasks.get(pending.task.id()) != pending) {
+                return false;
+            }
+            acquire = !pending.task.ephemeralTask() && pending.taskLock == 
null;
+            pending.cancellationDepth++;
+        }
+        boolean entered = false;
+        boolean coordinated = false;
+        try {
+            if (acquire) {
+                // Don't block publication/release of this runner's token 
during the lock RPC.
+                acquired = this.tryLockTask(pending.task.id().asString());
+                if (!acquired.lockSuccess()) {
+                    return false;
+                }
+                synchronized (pending) {
+                    pending.taskLock = acquired;
+                }
+            }
+            entered = true;
+            if (!pending.task.ephemeralTask()) {
+                HugeTask<?> stored;
+                try {
+                    stored = this.taskWithoutResult(pending.task.id());
+                } catch (NotFoundException e) {
+                    pending.discardQueued = true;
+                    coordinated = true;
+                    return false;
+                }
+                if (beforeCancel == null) {
+                    if 
(TaskStatus.COMPLETED_STATUSES.contains(stored.status())) {
+                        pending.discardQueued = true;
+                        coordinated = true;
+                        return false;
+                    }
+                    if (terminalOnly) {
+                        if (stored.status() == TaskStatus.DELETING) {
+                            pending.discardQueued = true;
+                            coordinated = true;
+                        } else if (pending.task.isCancelled() &&
+                                   pending.task.status() == 
TaskStatus.CANCELLED &&
+                                   stored.status() == TaskStatus.CANCELLING) {

Review Comment:
   The broad cancellation-persistence replay protocol has been deferred with 
task-record recovery. This version guarantees that physical execution, accepted 
local callbacks and native cleanup have finished before graph deletion; it does 
not guarantee recovery of every final task-status publication failure. 
Failed/uncertain scheduler cleanup retains the scheduler and graph instead of 
acknowledging deletion. Such storage/recovery failures require explicit 
maintenance, as documented.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to