bitflicker64 commented on code in PR #3329:
URL: https://github.com/apache/hugegraph/pull/3329#discussion_r4265613977


##########
hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/core/GraphManager.java:
##########
@@ -2274,53 +2352,63 @@ public void dropGraph(String graphSpace, String name, 
boolean clear) {
                              "Retry deletion after owners exit, or restart to 
resume task admission",
                              graphName);
             }
+            // Cleanup failure must precede metadata removal and destructive 
clear.
+            this.unregisterRpcServices(g);
             this.removingGraphs.add(graphName);
             try {
                 this.metaManager.removeGraphConfig(graphSpace, name);
                 this.metaManager.notifyGraphRemove(graphSpace, name);
             } catch (Exception e) {
+                // No backend clear has started; retain RPC for the same open 
local owner.
+                synchronized (this) {
+                    if (this.graphs.get(graphName) == g && !g.closed()) {
+                        try {
+                            this.registerRpcServices(g);
+                        } catch (RuntimeException | Error restoration) {
+                            if (e != restoration) {
+                                e.addSuppressed(restoration);
+                            }
+                        }
+                    }
+                }
                 throw new HugeException(
-                        "Failed to remove graph config of '%s'", name, e);
+                        "Failed to remove graph config of '%s'", e, name);
             }
 
             g.clearBackend();
-            try {
-                g.close();
-            } catch (Exception e) {
-                if (!g.closed()) {
-                    throw new HugeException("Failed to close graph '%s', 
please retry later", e, graphName);
+        }
+        try {
+            g.close();
+        } catch (RuntimeException | Error failure) {
+            throw failure;

Review Comment:
   Important: this catch changes two things `dropGraph` did on master when 
`g.close()` throws, and both leave a server worse off than before.
   
   1. The exception type. `StandardHugeGraph.close()` throws a plain 
`IllegalStateException` when a thread still has an open transaction or the 
scheduler is not drained (`StandardHugeGraph.java:1168-1170`). Master wrapped 
that in `HugeException("Failed to close graph '%s', please retry later")`. Here 
it is rethrown as is. `graphRemoveHandler` only catches `HugeException` around 
`this.dropGraph(parts[0], parts[1], false)` (`GraphManager.java:2773-2779`), so 
the first graph that cannot close now escapes the `for` loop and the remaining 
names in the same watch response are never dropped on this server. 
`PdMetaDriver.extractValuesFromResponse` returns one value per event, so a 
response can carry several graphs (for example after `clearGraphSpace`). 
`ConsumerWrapper.accept` logs the error and nothing replays it.
   
   2. The case where the graph did close. `close()` sets `closed = true` and 
then runs the second group of steps (`unregisterRpcServices`, the cache 
`closeGraph` calls, `storeProvider::close`, `LockUtil.destroy`), rethrowing the 
first failure at the end. Master logged that (`if (!g.closed()) throw ...; 
LOG.warn(...)`) and went on to remove the graph from `graphs` and 
`HugeFactory`, recycle the quota and fire `GRAPH_DROP`. Now the method exits 
here, after `removeGraphConfig` and `clearBackend()` have already run, and the 
closed graph stays in `graphs`, in `HugeFactory` and in the Gremlin bindings. A 
second DELETE cannot finish the job: with `clear=true` it stops at 
`g.taskScheduler()` (`E.checkState(scheduler != null, ...)`, the scheduler was 
removed by the successful drain) or at `clearBackend()` -> 
`checkGraphNotClosed()`. On a peer (`clear=false`) nobody retries at all, and 
`graphAddHandler` skips any name still present in `graphs`, so a graph created 
again under that name is never
  loaded on that server until restart.
   
   Requested change: keep master's handling for a graph that reports 
`closed()`: log the failure and continue into the synchronized block so the 
local entry, factory binding, quota and `GRAPH_DROP` are still cleaned up. For 
a graph that is not closed, keep throwing `HugeException` (or widen the catch 
in `graphRemoveHandler`) so one busy graph does not stop the rest of the batch. 
If the owner must be retained when RPC cleanup inside `close()` fails, the 
DELETE retry needs a path that works on an already closed graph, plus a test 
that retries with `clear=true`; `checkLegacyDrop` only retries the quota 
failure with `clear=false`.



##########
hugegraph-commons/hugegraph-rpc/src/main/java/org/apache/hugegraph/rpc/RpcProviderConfig.java:
##########
@@ -54,27 +55,54 @@ private <T, S extends T> String addService(String graph,
         providerConfig.setInterfaceId(interfaceId)
                       .setRef(serviceImpl);
 
-        E.checkArgument(!this.configs.containsKey(serviceId),
+        E.checkArgument(this.configs.putIfAbsent(serviceId, providerConfig) == 
null,
                         "Not allowed to add service already exist: '%s'",
                         serviceId);
-        this.configs.put(serviceId, providerConfig);
         return serviceId;
     }
 
     @Override
     public void removeService(String serviceId) {
-        ProviderConfig<?> config = this.configs.remove(serviceId);
-        E.checkArgument(config != null,
-                        "The service '%s' doesn't exist", serviceId);
-        config.unExport();
+        boolean[] removed = {false};
+        this.configs.computeIfPresent(serviceId, (key, config) -> {
+            config.unExport();
+            removed[0] = true;
+            return null;
+        });
+        E.checkArgument(removed[0], "The service '%s' doesn't exist", 
serviceId);
     }
 
     @Override
-    public void removeAllService() {
-        for (ProviderConfig<?> config : this.configs.values()) {
+    public boolean removeService(String serviceId, Object serviceImpl) {
+        boolean[] removed = {false};
+        this.configs.computeIfPresent(serviceId, (key, config) -> {
+            if (config.getRef() != serviceImpl) {
+                return config;
+            }
             config.unExport();
+            removed[0] = true;
+            return null;
+        });
+        return removed[0];
+    }
+
+    @Override
+    public void removeAllService() {
+        for (String serviceId : this.configs.keySet()) {
+            this.configs.computeIfPresent(serviceId, (key, config) -> {
+                config.unExport();
+                return null;
+            });
+        }
+    }
+
+    void exportAll(ServerConfig server, int timeout) {
+        for (String serviceId : this.configs.keySet()) {
+            this.configs.computeIfPresent(serviceId, (key, config) -> {
+                config.setServer(server).setTimeout(timeout).export();

Review Comment:
   Minor: `exportAll` now runs once for every graph published after startup 
(`GraphManager.registerRpcServices` calls `this.rpcServer.exportAll()`), and 
each run calls `config.setServer(server)` on every provider already in the map. 
In sofa-rpc 5.7.6 `ProviderConfig.setServer(ServerConfig)` appends 
(`this.server.add(server)`, `ProviderConfig.java:497-503`), and `export()` then 
returns early because the provider is already exported. So each provider's 
server list gains one duplicate entry per later publication, and `unExport()` 
later calls `unRegisterProcessor` once per duplicate. With N late graphs that 
is about N^2 list entries across the 2N notifier providers, which adds up on 
servers that lazily load many graphs from PD.
   
   Requested change: only set the server and timeout on configs that have not 
been exported yet (for example skip configs whose `getServer()` is already 
non-empty), or export just the two services the new graph added.



-- 
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