bitflicker64 commented on code in PR #357:
URL: 
https://github.com/apache/hugegraph-computer/pull/357#discussion_r4046533255


##########
computer/computer-core/src/main/java/org/apache/hugegraph/computer/core/sender/QueuedMessageSender.java:
##########
@@ -127,57 +173,75 @@ private class Sender implements Runnable {
         public void run() {
             LOG.info("The send-executor is running");
             Thread thread = Thread.currentThread();
-            while (!thread.isInterrupted()) {
-                try {
-                    int emptyQueueCount = 0;
-                    int busyClientCount = 0;
-                    for (WorkerChannel channel : channels) {
-                        QueuedMessage message = channel.queue.peek();
-                        if (message == null) {
-                            ++emptyQueueCount;
-                            continue;
+            try {
+                while (!thread.isInterrupted()) {
+                    try {
+                        int emptyQueueCount = 0;
+                        int busyClientCount = 0;
+                        for (WorkerChannel channel : channels) {
+                            QueuedMessage message = channel.queue.peek();
+                            if (message == null) {
+                                ++emptyQueueCount;
+                                continue;
+                            }
+                            try {
+                                if (channel.doSend(message)) {
+                                    // Only consume the message after it is 
sent
+                                    channel.queue.take();
+                                } else {
+                                    ++busyClientCount;
+                                }
+                            } catch (TransportException | RuntimeException e) {
+                                channel.failDataSend(e);
+                                // Discard the failed data message to keep 
sending
+                                channel.queue.take();
+                                LOG.warn("Failed to send {} message to {}, " +
+                                         "discard it", message.type(), 
channel, e);
+                            }
                         }
-                        if (channel.doSend(message)) {
-                            // Only consume the message after it is sent
-                            channel.queue.take();
-                        } else {
-                            ++busyClientCount;
+                        int channelCount = channels.length;
+                        /*
+                         * If all queues are empty, let send thread wait
+                         * until any queue is available
+                         */
+                        if (emptyQueueCount >= channelCount) {
+                            LOG.debug("The send executor was blocked " +
+                                      "to wait any queue not empty");
+                            QueuedMessageSender.this.waitAnyQueueNotEmpty();

Review Comment:
   🧹 The busy-wait path now treats an unexpected interrupt as fatal, but the 
idle path here doesn't. `waitAnyQueueNotEmpty()` swallows the interrupt and 
re-sets the flag, so the `while (!thread.isInterrupted())` check just ends the 
loop: the executor returns normally, nothing goes through `recordFatal()`, and 
`checkFatal()` keeps passing. The next START/FINISH is queued onto a dead 
thread and the caller waits out the full control timeout (80s for FINISH with 
defaults).
   
   Probe against this head (one worker, idle `MockTransportClient`, interrupt 
the executor while it is WAITING with `closed == false`, then send FINISH):
   
   ```
   state before interrupt = WAITING
   executor alive after interrupt = false
   checkFatal() = no error recorded
   FINISH still incomplete after 3005 ms
   ```
   
   Could `waitAnyQueueNotEmpty()` handle the interrupt the same way 
`waitAnyClientNotBusy()` does (return when `closed`, otherwise `recordFatal()` 
and throw)? A test next to `testFatalExecutorCompletesQueuedFinish` that 
interrupts an idle executor and asserts the next FINISH fails fast would cover 
it.



##########
computer/computer-core/src/main/java/org/apache/hugegraph/computer/core/network/DataServerManager.java:
##########
@@ -54,9 +54,8 @@ public void init(Config config) {
 
     @Override
     public void close(Config config) {
-        InetSocketAddress address = this.address();
         this.connectionManager.shutdownServer();
-        LOG.info("DataServerManager closed with address '{}'", address);
+        LOG.info("DataServerManager closed");

Review Comment:
   🧹 This makes `DataServerManager` safe to close after a partial init, but 
`Managers.closeAll()` still stops at the first manager that throws, and the 
last worker manager, `WorkerInputManager`, throws on the same path. If 
`initAll()` fails before `WorkerInputManager.init()` runs (port already in use 
in `DataServerManager.init()`, or a MinIO error in `SnapshotManager.init()`), 
`LoadService.fetchers` is still all nulls and `LoadService.close()` NPEs on 
`fetcher.close()`. `WorkerService.close()` then logs `Error while closing 
managers` with that NPE, and `WorkerInputManager`'s `sendExecutor.shutdown()` 
is skipped.
   
   Probe against this head (real `WorkerInputManager` with mocked send/snapshot 
managers, `close()` without `init()`):
   
   ```
   close threw: java.lang.NullPointerException
     at 
org.apache.hugegraph.computer.core.worker.load.LoadService.close(LoadService.java:82)
     at 
org.apache.hugegraph.computer.core.input.WorkerInputManager.close(WorkerInputManager.java:97)
   ```
   
   `testCloseManagersAfterPartialInitFailure` can't see this because `last` is 
a mock. Could `LoadService.close()` skip null fetchers (or 
`Managers.closeAll()` close each manager in its own try/catch and rethrow the 
first failure with the rest suppressed)? Registering a real 
`WorkerInputManager` after `DataServerManager` in that test would cover it.



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