Skip to content

Commit 5eb2fc0

Browse files
committed
fix(core): drain tasks before graph deletion
- fence admission and track actual execution owners - wait for callbacks and transaction cleanup before deletion - retain graphs and schedulers when draining fails - keep core regressions and defer reload coordination
1 parent 68fb045 commit 5eb2fc0

12 files changed

Lines changed: 1730 additions & 144 deletions

File tree

‎docs/server-runtime.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,3 +463,20 @@ Structure or Process suites must execute tests; an all-skipped report is not
463463
compatibility evidence. Run the read-only application examples against representative
464464
existing data and validate the serializers, client versions and authentication
465465
combinations actually used by the application.
466+
467+
468+
## Deleting a graph with local tasks
469+
470+
Stop application writes before deletion and keep them stopped during retries. The local
471+
scheduler closes task admission and waits for actual executions, cancellation callbacks
472+
and their transaction cleanup before data or metadata is removed. If draining times out,
473+
the graph stays registered with its data and configuration; retry after its owners exit.
474+
Closing the scheduler alone leaves persistent work that has not started in storage.
475+
476+
This is a local shutdown guarantee. For a graph shared by several Servers, stop writes
477+
and tasks on every Server before deletion; notification delivery is not a cluster-wide
478+
barrier. Do not recreate the same graph name until deletion finishes. Use a restart for
479+
configuration changes rather than hot reload, whose recovery is handled separately.
480+
Deletion is not transactional: a failure after destructive cleanup needs operator
481+
inspection, and retry does not restore removed data. An owner-cleanup failure must not
482+
be treated as a successful drain; inspect it instead of force-removing the scheduler.

‎hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/core/GraphManager.java‎

Lines changed: 41 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.util.Set;
3737
import java.util.concurrent.ConcurrentHashMap;
3838
import java.util.concurrent.TimeUnit;
39+
import java.util.concurrent.TimeoutException;
3940
import java.util.concurrent.atomic.AtomicInteger;
4041
import java.util.function.Consumer;
4142
import java.util.stream.Collectors;
@@ -114,6 +115,7 @@
114115
import org.apache.hugegraph.space.register.dto.ServiceDTO;
115116
import org.apache.hugegraph.space.register.registerImpl.PdRegister;
116117
import org.apache.hugegraph.task.TaskManager;
118+
import org.apache.hugegraph.task.TaskScheduler;
117119
import org.apache.hugegraph.testutil.Whitebox;
118120
import org.apache.hugegraph.traversal.optimize.HugeScriptTraversal;
119121
import org.apache.hugegraph.type.define.CollectionType;
@@ -1107,23 +1109,21 @@ public void getSpaceStorage(String graphSpace) {
11071109
}
11081110

11091111
public void clearGraphSpace(String name) {
1110-
// Clear all roles
1111-
this.metaManager.clearGraphAuth(name);
1112-
1113-
// Clear all schemaTemplate
1114-
this.metaManager.clearSchemaTemplate(name);
1115-
11161112
// Clear all graphs
11171113
for (String key : this.graphs.keySet()) {
1118-
if (key.startsWith(name)) {
1114+
if (key.startsWith(name + DELIMITER)) {
11191115
String[] parts = key.split(DELIMITER);
11201116
this.dropGraph(parts[0], parts[1], true);
11211117
}
11221118
}
11231119

1120+
// Shared metadata remains available if a graph refuses deletion.
1121+
this.metaManager.clearGraphAuth(name);
1122+
this.metaManager.clearSchemaTemplate(name);
1123+
11241124
// Clear all services
11251125
for (String key : this.services.keySet()) {
1126-
if (key.startsWith(name)) {
1126+
if (key.startsWith(name + DELIMITER)) {
11271127
String[] parts = key.split(DELIMITER);
11281128
this.dropService(parts[0], parts[1]);
11291129
}
@@ -1457,12 +1457,13 @@ private HugeGraph createGraphLocal(HugeConfig config, String name) {
14571457
LOG.error("Failed to create graph '{}' due to: {}",
14581458
name, e.getMessage(), e);
14591459
if (graph != null) {
1460-
this.graphs.remove(graph.spaceGraphName(), graph);
14611460
try {
14621461
this.dropGraphLocal(graph);
1463-
} finally {
1464-
// The create event may have partially registered the graph
1462+
this.graphs.remove(graph.spaceGraphName(), graph);
14651463
this.notifyEventLenient(Events.GRAPH_DROP, graph);
1464+
} catch (Throwable cleanup) {
1465+
e.addSuppressed(cleanup);
1466+
this.graphs.putIfAbsent(graph.spaceGraphName(), graph);
14661467
}
14671468
}
14681469
throw e;
@@ -1472,16 +1473,8 @@ private HugeGraph createGraphLocal(HugeConfig config, String name) {
14721473
}
14731474

14741475
private void dropGraphLocal(HugeGraph graph) {
1475-
try {
1476-
// Clear data and config files
1477-
graph.drop();
1478-
} finally {
1479-
/*
1480-
* Will fill graph instance into HugeFactory.graphs after
1481-
* GraphFactory.open() succeed, remove it when the graph drops
1482-
*/
1483-
HugeFactory.remove(graph);
1484-
}
1476+
graph.drop();
1477+
HugeFactory.remove(graph);
14851478
}
14861479

14871480
public HugeGraph createGraph(String graphSpace, String name, String creator,
@@ -1614,16 +1607,14 @@ public HugeGraph createGraph(String graphSpace, String name, String creator,
16141607
try {
16151608
this.notifyEvent(Events.GRAPH_CREATE, graph);
16161609
} catch (Throwable e) {
1617-
this.notifyEventLenient(Events.GRAPH_DROP, graph);
1618-
this.graphs.remove(graphName, graph);
16191610
try {
16201611
graph.close();
1612+
this.notifyEventLenient(Events.GRAPH_DROP, graph);
1613+
this.graphs.remove(graphName, graph);
1614+
HugeFactory.remove(graph);
16211615
} catch (Exception e1) {
1622-
if (graph instanceof StandardHugeGraph) {
1623-
((StandardHugeGraph) graph).clearSchedulerAndLock();
1624-
}
1616+
e.addSuppressed(e1);
16251617
}
1626-
HugeFactory.remove(graph);
16271618
throw e;
16281619
}
16291620

@@ -2086,12 +2077,11 @@ private HugeGraph createGraph(String graphSpace, HugeConfig config,
20862077
} catch (BackendException e) {
20872078
try {
20882079
graph.close();
2080+
HugeFactory.remove(graph);
20892081
} catch (Exception e1) {
2090-
if (graph instanceof StandardHugeGraph) {
2091-
((StandardHugeGraph) graph).clearSchedulerAndLock();
2092-
}
2082+
e.addSuppressed(e1);
2083+
this.graphs.putIfAbsent(graph.spaceGraphName(), graph);
20932084
}
2094-
HugeFactory.remove(graph);
20952085
throw e;
20962086
}
20972087
}
@@ -2269,6 +2259,18 @@ public void dropGraph(String graphSpace, String name, boolean clear) {
22692259

22702260
String graphName = spaceGraphName(graphSpace, name);
22712261
if (clear) {
2262+
// The auth proxy checks ADMIN before the local deletion fence.
2263+
TaskScheduler scheduler = g.taskScheduler();
2264+
if (!scheduler.close()) {
2265+
try {
2266+
scheduler.waitUntilAllTasksCompleted(g.configuration().get(CoreOptions.TASK_WAIT_TIMEOUT));
2267+
} catch (TimeoutException e) {
2268+
throw new HugeException("Tasks are still active in graph '%s', please retry later",
2269+
e, graphName);
2270+
}
2271+
E.checkState(scheduler.close(), "Tasks are still active in graph '%s', please retry later",
2272+
graphName);
2273+
}
22722274
this.removingGraphs.add(graphName);
22732275
try {
22742276
this.metaManager.removeGraphConfig(graphSpace, name);
@@ -2278,24 +2280,13 @@ public void dropGraph(String graphSpace, String name, boolean clear) {
22782280
"Failed to remove graph config of '%s'", name, e);
22792281
}
22802282

2281-
/**
2282-
* close task scheduler before clear data,
2283-
* because taskinfo stored in backend in
2284-
* {@link org.apache.hugegraph.task.DistributedTaskScheduler}
2285-
*/
2286-
try {
2287-
g.taskScheduler().close();
2288-
} catch (Throwable t) {
2289-
LOG.warn(String.format(
2290-
"Error when close TaskScheduler of %s",
2291-
graphName),
2292-
t);
2293-
}
2294-
22952283
g.clearBackend();
22962284
try {
22972285
g.close();
22982286
} catch (Exception e) {
2287+
if (!g.closed()) {
2288+
throw new HugeException("Failed to close graph '%s', please retry later", e, graphName);
2289+
}
22992290
LOG.warn("Failed to close graph", e);
23002291
}
23012292
}
@@ -2309,13 +2300,17 @@ public void dropGraph(String graphSpace, String name, boolean clear) {
23092300
}
23102301
// Let gremlin server and rest server context remove graph
23112302
LOG.info("Notify remove graph {} by GRAPH_DROP event", name);
2312-
Graph graph = this.graphs.remove(graphName);
2303+
Graph graph = this.graphs.get(graphName);
23132304
if (graph != null) {
23142305
try {
23152306
graph.close();
23162307
} catch (Exception e) {
2308+
if (!((HugeGraph) graph).closed()) {
2309+
throw new HugeException("Failed to close graph '%s', please retry later", e, graphName);
2310+
}
23172311
LOG.warn("Failed to close graph", e);
23182312
}
2313+
this.graphs.remove(graphName, graph);
23192314
try {
23202315
// Delete alias graph in HugeFactory
23212316
HugeFactory.remove((HugeGraph) graph);

‎hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/StandardHugeGraph.java‎

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,7 @@ public void initBackend() {
537537

538538
@Override
539539
public void clearBackend() {
540+
this.checkGraphNotClosed();
540541
this.waitUntilAllTasksCompleted();
541542

542543
this.loadSchemaStore().open(this.configuration);
@@ -1212,12 +1213,16 @@ public void create(String configPath, GlobalMasterInfo nodeInfo) {
12121213

12131214
@Override
12141215
public void drop() {
1216+
this.checkGraphNotClosed();
1217+
TaskScheduler scheduler = this.taskManager.getScheduler(this.params);
1218+
if (scheduler != null && !scheduler.close()) {
1219+
// Fence admission before waiting for actual task owners to exit.
1220+
this.waitUntilAllTasksCompleted();
1221+
E.checkState(scheduler.close(), "Tasks are still active in graph '%s', please retry later",
1222+
this.spaceGraphName());
1223+
}
12151224
this.clearBackend();
12161225

1217-
HugeConfig config = this.configuration();
1218-
this.storeProvider.onDeleteConfig(config);
1219-
ConfigUtil.deleteFile(config.file());
1220-
12211226
try {
12221227
/*
12231228
* It's hard to ensure all threads close the tx.
@@ -1228,8 +1233,15 @@ public void drop() {
12281233
*/
12291234
this.close();
12301235
} catch (Throwable e) {
1236+
if (!this.closed()) {
1237+
throw new HugeException("Failed to close graph '%s', please retry later",
1238+
e, this.spaceGraphName());
1239+
}
12311240
LOG.warn("Failed to close graph {} {}", this, e);
12321241
}
1242+
HugeConfig config = this.configuration();
1243+
this.storeProvider.onDeleteConfig(config);
1244+
ConfigUtil.deleteFile(config.file());
12331245
}
12341246

12351247
@Override

0 commit comments

Comments
 (0)