github-actions[bot] commented on code in PR #69421:
URL: https://github.com/apache/doris/pull/69421#discussion_r4344451563


##########
fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java:
##########
@@ -779,6 +779,30 @@ public void setCommand(MysqlCommand command) {
         this.command = command;
     }
 
+    /** Reserve an idle MySQL connection before dispatching a newly received 
packet. */
+    public synchronized boolean startMysqlCommand() {
+        if (isKilled) {
+            return false;
+        }
+        // COM_QUERY also covers the short interval before dispatch decodes 
the command.
+        // Otherwise the shutdown thread could mistake a received packet for 
an idle socket.
+        command = MysqlCommand.COM_QUERY;
+        return true;
+    }
+
+    /** Close an idle socket without cancelling an executor that has just 
finished. */
+    public boolean closeIdleMysqlConnection() {
+        synchronized (this) {
+            if (getConnectType() != ConnectType.MYSQL || command != 
MysqlCommand.COM_SLEEP || isKilled) {

Review Comment:
   [P2] Keep the busy reservation through reset response finalization
   
   An admitted COM_RESET_CONNECTION can still be closed as idle here. Its 
dispatch calls ConnectContext.resetConnection(), which sets command = COM_SLEEP 
before handleResetConnection() sets OK and before processOnce() calls 
finalizeCommand() to send the response. If the shutdown thread scans the 
connection in that interval, this predicate marks it killed and closes the 
socket even though its request is still counted as active. The client loses the 
admitted reset's reply. Preserve a separate processing reservation through 
response finalization, or keep resetConnection() from publishing COM_SLEEP 
before that point.



##########
fe/fe-core/src/main/java/org/apache/doris/qe/ConnectScheduler.java:
##########
@@ -124,6 +124,22 @@ public Map<Integer, ConnectContext> getConnectionMap() {
         return connectPoolMgr.getConnectionMap();
     }
 
+    public void closeIdleMysqlConnections() {
+        for (ConnectContext context : getConnectionMap().values()) {
+            try {
+                context.closeIdleMysqlConnection();
+            } catch (Exception e) {
+                LOG.warn("failed to close idle MySQL connection {}", 
context.getConnectionId(), e);
+            }
+        }
+    }
+
+    public boolean hasPendingFlightResults() {
+        return getConnectionMap().values().stream()
+                .anyMatch(context -> context.getConnectType() == 
ConnectContext.ConnectType.ARROW_FLIGHT_SQL
+                        && context.getFlightSqlChannel().resultNum() > 0);

Review Comment:
   [P2] Exclude expired Flight entries from the pending-result check
   
   resultNum() returns Guava Cache.size(), which [can include expired 
entries](https://guava.dev/releases/33.2.1-jre/api/docs/com/google/common/cache/CacheBuilder.html).
 For example, leave a Flight session open after abandoning a FE-local result, 
then shut down more than ten minutes later. The result has expired, but the 
session can remain for the default eight-hour wait_timeout, and neither the 
session timeout checker nor repeated size() calls cleans up this cache. This 
predicate can therefore remain true until the full grace_shutdown_wait_seconds 
elapses (300 seconds by default) even though no result can be retrieved and no 
request or coordinator remains. Remove expired entries or inspect only live 
results before using cache occupancy to hold shutdown open.



##########
fe/fe-core/src/main/java/org/apache/doris/DorisFE.java:
##########
@@ -726,19 +727,46 @@ public static boolean isServerReady() {
     }
 
     private static void gracefulShutdown() {
-        // wait for all queries to finish
+        // Health is already 503, but keep serving during LB / DNS convergence.
         try {
-            long now = System.currentTimeMillis();
-            List<Coordinator> allCoordinators = 
QeProcessorImpl.INSTANCE.getAllCoordinators();
-            while (!allCoordinators.isEmpty() && System.currentTimeMillis() - 
now < 300 * 1000L) {
-                Thread.sleep(1000);
-                allCoordinators = 
QeProcessorImpl.INSTANCE.getAllCoordinators();
-                LOG.info("waiting {} queries to finish before shutdown", 
allCoordinators.size());
+            LOG.info("graceful shutdown: serving during drain for {} seconds", 
Config.graceful_shutdown_drain_seconds);
+            TimeUnit.SECONDS.sleep(Math.max(0, 
Config.graceful_shutdown_drain_seconds));

Review Comment:
   [P2] Skip the drain delay for processes that never served requests
   
   The shutdown hook is installed before checkCommandLineOptions(), so 
bin/start_fe.sh --version reaches System.exit(0) and this sleep while all 
service listeners are still uninitialized. The same happens for diagnostic-tool 
exits and startup failures after hook registration. These paths now wait 15 
seconds by default, or the entire configured drain interval, despite never 
having served traffic that needs load-balancer convergence. Apply this delay 
only when the process had actually entered service, retaining that state before 
the hook clears serverReady.



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