platinumhamburg commented on code in PR #3651:
URL: https://github.com/apache/fluss/pull/3651#discussion_r3600179920


##########
fluss-server/src/main/java/org/apache/fluss/server/tablet/TabletServer.java:
##########
@@ -465,21 +478,8 @@ CompletableFuture<Void> stopServices() {
                     scannerManager.close();
                 }
 
-                if (kvManager != null) {
-                    kvManager.shutdown();
-                }
-
-                if (remoteLogManager != null) {
-                    remoteLogManager.close();
-                }
-
-                if (logManager != null) {
-                    logManager.shutdown();
-                }
-
-                if (replicaManager != null) {
-                    replicaManager.shutdown();
-                }
+                shutdownReplicaManager();
+                shutdownTabletManagers();
 

Review Comment:
   These calls are in the same `try` block, so any exception from 
`shutdownReplicaManager()` skips `shutdownTabletManagers()` and every cleanup 
step after it.
   
   One concrete path is `checkpointHighWatermarks()`: a checkpoint write 
failure in one data directory is thrown after replica activity has already been 
stopped, but it still prevents KV and log tablets in every directory from being 
closed.
   
   This deterministically turns the TabletServer shutdown into an unclean 
shutdown. Local logs are not force-flushed, recovery checkpoints and 
clean-shutdown markers are not written, and even healthy data directories must 
go through unclean recovery on the next startup.
   
   Please preserve the shutdown order but handle these steps separately: record 
the ReplicaManager failure, continue shutting down the tablet managers and 
remaining resources, and report the aggregated failure afterward. Please also 
add a regression test where `shutdownReplicaManager()` fails and verify that 
`shutdownTabletManagers()` still runs.



##########
fluss-server/src/main/java/org/apache/fluss/server/ServerBase.java:
##########
@@ -177,15 +181,19 @@ public void onFatalError(Throwable exception) {
                 exception);
         if (ExceptionUtils.isJvmFatalError(exception)) {
             System.exit(-1);
-        } else {
-            closeAsync(Result.FAILURE);
+        } else if (fatalErrorShutdownTriggered.compareAndSet(false, true)) {
             FutureUtils.orTimeout(
                     getTerminationFuture(),
                     FATAL_ERROR_SHUTDOWN_TIMEOUT_MS,
                     TimeUnit.MILLISECONDS,
                     String.format(
                             "Waiting for %s shutting down timed out after %s 
ms.",
                             getServerName(), FATAL_ERROR_SHUTDOWN_TIMEOUT_MS));
+
+            Thread shutdownThread =
+                    new ExecutorThreadFactory(getServerName() + 
"-fatal-error-shutdown")
+                            .newThread(() -> closeAsync(Result.FAILURE));
+            shutdownThread.start();

Review Comment:
   `isJvmFatalError()` does not include `OutOfMemoryError`, so an OOM—including 
“unable to create new native thread”—takes this branch.
   
   The one-shot shutdown flag is set before the new thread is created. If 
`newThread()` or `start()` throws, shutdown never starts, and later fatal-error 
callbacks are ignored because the flag is already set.
   
   Please treat OOM as immediately fatal by using 
`isJvmFatalOrOutOfMemoryError()`, or catch thread creation/start failures and 
terminate the process as a fallback. Please also add a test covering this 
failure path.



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

Reply via email to