Copilot commented on code in PR #17181:
URL: https://github.com/apache/iotdb/pull/17181#discussion_r2785404263


##########
iotdb-client/cli/src/main/java/org/apache/iotdb/cli/AbstractCli.java:
##########
@@ -611,6 +676,18 @@ private static int executeQuery(CliContext ctx, 
IoTDBConnection connection, Stri
       } else {
         ctx.getPrinter().println("Msg: " + SUCCESS_MESSAGE);
       }
+    } catch (SQLException e) {
+      if (isConnectionRelated(e)) {
+        throw e;
+      }
+      if (isSessionOrStatementError(e)) {
+        ctx.getPrinter()
+            .println(
+                "Reconnected, but the previous command could not be completed. 
Please run your command again.");

Review Comment:
   `executeQuery` prints "Reconnected, but the previous command could not be 
completed..." for session/statement-state errors, but this method cannot know 
whether a reconnect actually happened. If the same server-side error occurs 
without any reconnect, this message is misleading. Suggestion: make the message 
not assume reconnection (e.g., "Session state was reset..."), or only print 
this in the reconnect-and-retry path where reconnection is known to have 
occurred.
   ```suggestion
                   "Session state was reset and the previous command could not 
be completed. Please run your command again.");
   ```



##########
iotdb-client/cli/src/main/java/org/apache/iotdb/cli/Cli.java:
##########
@@ -195,36 +247,87 @@ private static void executeSql(CliContext ctx) throws 
TException {
   }
 
   private static void receiveCommands(CliContext ctx) throws TException {
-    try (IoTDBConnection connection =
-        (IoTDBConnection)
-            DriverManager.getConnection(Config.IOTDB_URL_PREFIX + host + ":" + 
port + "/", info)) {
-      connection.setQueryTimeout(queryTimeout);
-      properties = connection.getServerProperties();
-      
AGGREGRATE_TIME_LIST.addAll(properties.getSupportedTimeAggregationOperations());
-      timestampPrecision = properties.getTimestampPrecision();
-
+    IoTDBConnection connection = null;
+    try {
+      connection = openConnection();
+      setupConnection(connection);
       echoStarting(ctx);
       displayLogo(ctx, properties.getLogo(), properties.getVersion(), 
properties.getBuildInfo());
       ctx.getPrinter().println(String.format("Successfully login at %s:%s", 
host, port));
       while (true) {
-        boolean readLine = readerReadLine(ctx, connection);
-        if (readLine) {
+        ReadLineResult result = readerReadLine(ctx, connection);
+        if (result.stop) {
           break;
         }
+        if (result.failedCommand != null) {
+          // Connection failed during processCommand; try to reconnect and 
retry the command.
+          closeConnectionQuietly(connection);
+          connection = null;
+          boolean reconnected = false;
+          for (int attempt = 1; attempt <= RECONNECT_RETRY_NUM; attempt++) {
+            if (attempt > 1) {
+              try {
+                Thread.sleep(RECONNECT_RETRY_INTERVAL_MS);
+              } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                ctx.getErr().printf("%s: Reconnection interrupted.%n", 
IOTDB_ERROR_PREFIX);
+                ctx.exit(CODE_ERROR);
+              }
+            }
+            try {
+              connection = openConnection();
+              setupConnection(connection);
+              ctx.getPrinter().println("Connection lost. Reconnected. Retrying 
command.");
+              processCommand(ctx, result.failedCommand, connection);
+              reconnected = true;
+              break;
+            } catch (SQLException e) {
+              if (isSessionOrStatementError(e)) {
+                // Reconnect succeeded but retry failed due to 
session/statement state; ask user to
+                // run the command again.
+                ctx.getPrinter()
+                    .println(
+                        "Reconnected, but the previous command could not be 
completed. Please run your command again.");
+                reconnected = true;
+                break;
+              }
+              if (attempt == RECONNECT_RETRY_NUM) {
+                ctx.getErr()
+                    .printf(
+                        "%s: Could not reconnect after %d attempts. Please 
check that the server is running and try again.%n",
+                        IOTDB_ERROR_PREFIX, RECONNECT_RETRY_NUM);
+                ctx.exit(CODE_ERROR);
+              }
+            }

Review Comment:
   On reconnect attempts, if `openConnection()` succeeds but 
`setupConnection()` or `processCommand()` throws, the newly created 
`connection` is not closed before the next attempt (the variable is overwritten 
on the next iteration). This can leak sockets/sessions across retries. 
Suggestion: in the catch path, call `closeConnectionQuietly(connection)` and 
set `connection = null` before continuing to the next attempt.



##########
iotdb-client/cli/src/main/java/org/apache/iotdb/cli/Cli.java:
##########
@@ -195,36 +247,87 @@ private static void executeSql(CliContext ctx) throws 
TException {
   }
 
   private static void receiveCommands(CliContext ctx) throws TException {
-    try (IoTDBConnection connection =
-        (IoTDBConnection)
-            DriverManager.getConnection(Config.IOTDB_URL_PREFIX + host + ":" + 
port + "/", info)) {
-      connection.setQueryTimeout(queryTimeout);
-      properties = connection.getServerProperties();
-      
AGGREGRATE_TIME_LIST.addAll(properties.getSupportedTimeAggregationOperations());
-      timestampPrecision = properties.getTimestampPrecision();
-
+    IoTDBConnection connection = null;
+    try {
+      connection = openConnection();
+      setupConnection(connection);
       echoStarting(ctx);
       displayLogo(ctx, properties.getLogo(), properties.getVersion(), 
properties.getBuildInfo());
       ctx.getPrinter().println(String.format("Successfully login at %s:%s", 
host, port));
       while (true) {
-        boolean readLine = readerReadLine(ctx, connection);
-        if (readLine) {
+        ReadLineResult result = readerReadLine(ctx, connection);
+        if (result.stop) {
           break;
         }
+        if (result.failedCommand != null) {
+          // Connection failed during processCommand; try to reconnect and 
retry the command.
+          closeConnectionQuietly(connection);
+          connection = null;
+          boolean reconnected = false;
+          for (int attempt = 1; attempt <= RECONNECT_RETRY_NUM; attempt++) {
+            if (attempt > 1) {
+              try {
+                Thread.sleep(RECONNECT_RETRY_INTERVAL_MS);
+              } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                ctx.getErr().printf("%s: Reconnection interrupted.%n", 
IOTDB_ERROR_PREFIX);
+                ctx.exit(CODE_ERROR);
+              }
+            }
+            try {
+              connection = openConnection();
+              setupConnection(connection);
+              ctx.getPrinter().println("Connection lost. Reconnected. Retrying 
command.");
+              processCommand(ctx, result.failedCommand, connection);
+              reconnected = true;
+              break;

Review Comment:
   The reconnect retry `try { connection = openConnection(); 
setupConnection(connection); ... }` can throw `TException` from 
`setupConnection()`, but the retry loop only catches `SQLException`. This means 
some connection-loss cases can bypass the reconnect logic and bubble out 
immediately. Suggestion: include `TException` (or a common supertype) in the 
reconnect-attempt catch and handle it as a reconnect failure (with the same 
retry/backoff), while still distinguishing non-connection SQL errors from 
command execution.



##########
iotdb-client/cli/src/main/java/org/apache/iotdb/cli/Cli.java:
##########
@@ -195,36 +247,87 @@ private static void executeSql(CliContext ctx) throws 
TException {
   }
 
   private static void receiveCommands(CliContext ctx) throws TException {
-    try (IoTDBConnection connection =
-        (IoTDBConnection)
-            DriverManager.getConnection(Config.IOTDB_URL_PREFIX + host + ":" + 
port + "/", info)) {
-      connection.setQueryTimeout(queryTimeout);
-      properties = connection.getServerProperties();
-      
AGGREGRATE_TIME_LIST.addAll(properties.getSupportedTimeAggregationOperations());
-      timestampPrecision = properties.getTimestampPrecision();
-
+    IoTDBConnection connection = null;
+    try {
+      connection = openConnection();
+      setupConnection(connection);
       echoStarting(ctx);
       displayLogo(ctx, properties.getLogo(), properties.getVersion(), 
properties.getBuildInfo());
       ctx.getPrinter().println(String.format("Successfully login at %s:%s", 
host, port));
       while (true) {
-        boolean readLine = readerReadLine(ctx, connection);
-        if (readLine) {
+        ReadLineResult result = readerReadLine(ctx, connection);
+        if (result.stop) {
           break;
         }
+        if (result.failedCommand != null) {
+          // Connection failed during processCommand; try to reconnect and 
retry the command.
+          closeConnectionQuietly(connection);
+          connection = null;
+          boolean reconnected = false;
+          for (int attempt = 1; attempt <= RECONNECT_RETRY_NUM; attempt++) {
+            if (attempt > 1) {
+              try {
+                Thread.sleep(RECONNECT_RETRY_INTERVAL_MS);
+              } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                ctx.getErr().printf("%s: Reconnection interrupted.%n", 
IOTDB_ERROR_PREFIX);
+                ctx.exit(CODE_ERROR);
+              }
+            }
+            try {
+              connection = openConnection();
+              setupConnection(connection);
+              ctx.getPrinter().println("Connection lost. Reconnected. Retrying 
command.");
+              processCommand(ctx, result.failedCommand, connection);
+              reconnected = true;
+              break;

Review Comment:
   In the reconnect-and-retry path, the return value of `processCommand(...)` 
is ignored. If the retried input contains an `exit`/`quit` statement (or 
otherwise returns `false`), the CLI will continue the main loop instead of 
stopping as it normally would. Suggestion: capture the boolean return from 
`processCommand` and convert it into a `result.stop` / break out of the outer 
loop when it indicates the user requested exit.



##########
iotdb-client/cli/src/main/java/org/apache/iotdb/cli/Cli.java:
##########
@@ -195,36 +247,87 @@ private static void executeSql(CliContext ctx) throws 
TException {
   }
 
   private static void receiveCommands(CliContext ctx) throws TException {
-    try (IoTDBConnection connection =
-        (IoTDBConnection)
-            DriverManager.getConnection(Config.IOTDB_URL_PREFIX + host + ":" + 
port + "/", info)) {
-      connection.setQueryTimeout(queryTimeout);
-      properties = connection.getServerProperties();
-      
AGGREGRATE_TIME_LIST.addAll(properties.getSupportedTimeAggregationOperations());
-      timestampPrecision = properties.getTimestampPrecision();
-
+    IoTDBConnection connection = null;
+    try {
+      connection = openConnection();
+      setupConnection(connection);
       echoStarting(ctx);
       displayLogo(ctx, properties.getLogo(), properties.getVersion(), 
properties.getBuildInfo());
       ctx.getPrinter().println(String.format("Successfully login at %s:%s", 
host, port));
       while (true) {
-        boolean readLine = readerReadLine(ctx, connection);
-        if (readLine) {
+        ReadLineResult result = readerReadLine(ctx, connection);
+        if (result.stop) {
           break;
         }
+        if (result.failedCommand != null) {
+          // Connection failed during processCommand; try to reconnect and 
retry the command.
+          closeConnectionQuietly(connection);
+          connection = null;
+          boolean reconnected = false;
+          for (int attempt = 1; attempt <= RECONNECT_RETRY_NUM; attempt++) {
+            if (attempt > 1) {
+              try {
+                Thread.sleep(RECONNECT_RETRY_INTERVAL_MS);
+              } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                ctx.getErr().printf("%s: Reconnection interrupted.%n", 
IOTDB_ERROR_PREFIX);
+                ctx.exit(CODE_ERROR);
+              }
+            }
+            try {
+              connection = openConnection();
+              setupConnection(connection);
+              ctx.getPrinter().println("Connection lost. Reconnected. Retrying 
command.");
+              processCommand(ctx, result.failedCommand, connection);
+              reconnected = true;
+              break;
+            } catch (SQLException e) {
+              if (isSessionOrStatementError(e)) {
+                // Reconnect succeeded but retry failed due to 
session/statement state; ask user to
+                // run the command again.
+                ctx.getPrinter()
+                    .println(
+                        "Reconnected, but the previous command could not be 
completed. Please run your command again.");
+                reconnected = true;
+                break;
+              }
+              if (attempt == RECONNECT_RETRY_NUM) {
+                ctx.getErr()
+                    .printf(
+                        "%s: Could not reconnect after %d attempts. Please 
check that the server is running and try again.%n",
+                        IOTDB_ERROR_PREFIX, RECONNECT_RETRY_NUM);
+                ctx.exit(CODE_ERROR);
+              }
+            }
+          }
+          if (!reconnected) {
+            break;
+          }
+        }
       }
     } catch (SQLException e) {
       ctx.getErr().printf("%s: %s%n", IOTDB_ERROR_PREFIX, e.getMessage());
       ctx.exit(CODE_ERROR);
+    } finally {
+      closeConnectionQuietly(connection);
     }
   }
 
-  private static boolean readerReadLine(CliContext ctx, IoTDBConnection 
connection) {
+  private static ReadLineResult readerReadLine(CliContext ctx, IoTDBConnection 
connection) {
     String s;
     try {
       s = ctx.getLineReader().readLine(cliPrefix + "> ", null);
-      boolean continues = processCommand(ctx, s, connection);
-      if (!continues) {
-        return true;
+      try {
+        boolean continues = processCommand(ctx, s, connection);
+        if (!continues) {
+          return ReadLineResult.stopLoop();
+        }
+      } catch (SQLException e) {
+        if (isConnectionRelated(e)) {
+          return ReadLineResult.reconnectAndRetry(s);
+        }

Review Comment:
   When a connection-related SQLException occurs, the CLI retries the entire 
raw input line `s`. If the line contains multiple statements separated by `;`, 
some statements may already have executed successfully before the failure, and 
retrying the whole line can re-run those statements (duplicate writes / side 
effects). Suggestion: either only retry the specific statement that failed 
(track progress inside `processCommand`), or disable auto-retry for 
multi-statement input and ask the user to rerun manually.



##########
iotdb-client/cli/src/main/java/org/apache/iotdb/cli/Cli.java:
##########
@@ -155,6 +184,29 @@ private static boolean parseCommandLine(
     return true;
   }
 
+  private static IoTDBConnection openConnection() throws SQLException {
+    return (IoTDBConnection)
+        DriverManager.getConnection(Config.IOTDB_URL_PREFIX + host + ":" + 
port + "/", info);
+  }
+
+  private static void setupConnection(IoTDBConnection connection)
+      throws java.sql.SQLException, org.apache.thrift.TException {
+    connection.setQueryTimeout(queryTimeout);
+    properties = connection.getServerProperties();

Review Comment:
   `setupConnection()` appends to `AGGREGRATE_TIME_LIST` every time a reconnect 
happens, but the list is static and never cleared/deduped. During repeated 
reconnects this will grow unbounded and may introduce duplicate entries. 
Suggestion: clear the list before adding, or change it to a `Set` (if ordering 
is not required).
   ```suggestion
       properties = connection.getServerProperties();
       AGGREGRATE_TIME_LIST.clear();
   ```



##########
iotdb-client/cli/src/main/java/org/apache/iotdb/cli/Cli.java:
##########
@@ -195,36 +247,87 @@ private static void executeSql(CliContext ctx) throws 
TException {
   }
 
   private static void receiveCommands(CliContext ctx) throws TException {
-    try (IoTDBConnection connection =
-        (IoTDBConnection)
-            DriverManager.getConnection(Config.IOTDB_URL_PREFIX + host + ":" + 
port + "/", info)) {
-      connection.setQueryTimeout(queryTimeout);
-      properties = connection.getServerProperties();
-      
AGGREGRATE_TIME_LIST.addAll(properties.getSupportedTimeAggregationOperations());
-      timestampPrecision = properties.getTimestampPrecision();
-
+    IoTDBConnection connection = null;
+    try {
+      connection = openConnection();
+      setupConnection(connection);
       echoStarting(ctx);
       displayLogo(ctx, properties.getLogo(), properties.getVersion(), 
properties.getBuildInfo());
       ctx.getPrinter().println(String.format("Successfully login at %s:%s", 
host, port));
       while (true) {
-        boolean readLine = readerReadLine(ctx, connection);
-        if (readLine) {
+        ReadLineResult result = readerReadLine(ctx, connection);
+        if (result.stop) {
           break;
         }
+        if (result.failedCommand != null) {
+          // Connection failed during processCommand; try to reconnect and 
retry the command.
+          closeConnectionQuietly(connection);
+          connection = null;
+          boolean reconnected = false;
+          for (int attempt = 1; attempt <= RECONNECT_RETRY_NUM; attempt++) {
+            if (attempt > 1) {
+              try {
+                Thread.sleep(RECONNECT_RETRY_INTERVAL_MS);
+              } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                ctx.getErr().printf("%s: Reconnection interrupted.%n", 
IOTDB_ERROR_PREFIX);
+                ctx.exit(CODE_ERROR);
+              }
+            }
+            try {
+              connection = openConnection();
+              setupConnection(connection);
+              ctx.getPrinter().println("Connection lost. Reconnected. Retrying 
command.");
+              processCommand(ctx, result.failedCommand, connection);
+              reconnected = true;
+              break;
+            } catch (SQLException e) {
+              if (isSessionOrStatementError(e)) {
+                // Reconnect succeeded but retry failed due to 
session/statement state; ask user to
+                // run the command again.
+                ctx.getPrinter()
+                    .println(
+                        "Reconnected, but the previous command could not be 
completed. Please run your command again.");
+                reconnected = true;
+                break;
+              }
+              if (attempt == RECONNECT_RETRY_NUM) {
+                ctx.getErr()
+                    .printf(
+                        "%s: Could not reconnect after %d attempts. Please 
check that the server is running and try again.%n",
+                        IOTDB_ERROR_PREFIX, RECONNECT_RETRY_NUM);
+                ctx.exit(CODE_ERROR);
+              }
+            }
+          }
+          if (!reconnected) {
+            break;
+          }
+        }
       }
     } catch (SQLException e) {
       ctx.getErr().printf("%s: %s%n", IOTDB_ERROR_PREFIX, e.getMessage());
       ctx.exit(CODE_ERROR);
+    } finally {
+      closeConnectionQuietly(connection);
     }
   }
 
-  private static boolean readerReadLine(CliContext ctx, IoTDBConnection 
connection) {
+  private static ReadLineResult readerReadLine(CliContext ctx, IoTDBConnection 
connection) {
     String s;
     try {
       s = ctx.getLineReader().readLine(cliPrefix + "> ", null);
-      boolean continues = processCommand(ctx, s, connection);
-      if (!continues) {
-        return true;
+      try {
+        boolean continues = processCommand(ctx, s, connection);
+        if (!continues) {
+          return ReadLineResult.stopLoop();
+        }
+      } catch (SQLException e) {
+        if (isConnectionRelated(e)) {
+          return ReadLineResult.reconnectAndRetry(s);
+        }

Review Comment:
   The reconnection trigger/loop is a significant behavior change but there are 
no unit tests exercising it (e.g., simulating a connection-related SQLException 
from `processCommand`, verifying retry count/backoff, and ensuring 
non-connection SQLExceptions do not trigger reconnect or an incorrect "Could 
not reconnect" exit). Consider adding focused tests around this new flow to 
prevent regressions.



##########
iotdb-client/cli/src/main/java/org/apache/iotdb/cli/Cli.java:
##########
@@ -195,36 +247,87 @@ private static void executeSql(CliContext ctx) throws 
TException {
   }
 
   private static void receiveCommands(CliContext ctx) throws TException {
-    try (IoTDBConnection connection =
-        (IoTDBConnection)
-            DriverManager.getConnection(Config.IOTDB_URL_PREFIX + host + ":" + 
port + "/", info)) {
-      connection.setQueryTimeout(queryTimeout);
-      properties = connection.getServerProperties();
-      
AGGREGRATE_TIME_LIST.addAll(properties.getSupportedTimeAggregationOperations());
-      timestampPrecision = properties.getTimestampPrecision();
-
+    IoTDBConnection connection = null;
+    try {
+      connection = openConnection();
+      setupConnection(connection);
       echoStarting(ctx);
       displayLogo(ctx, properties.getLogo(), properties.getVersion(), 
properties.getBuildInfo());
       ctx.getPrinter().println(String.format("Successfully login at %s:%s", 
host, port));
       while (true) {
-        boolean readLine = readerReadLine(ctx, connection);
-        if (readLine) {
+        ReadLineResult result = readerReadLine(ctx, connection);
+        if (result.stop) {
           break;
         }
+        if (result.failedCommand != null) {
+          // Connection failed during processCommand; try to reconnect and 
retry the command.
+          closeConnectionQuietly(connection);
+          connection = null;
+          boolean reconnected = false;
+          for (int attempt = 1; attempt <= RECONNECT_RETRY_NUM; attempt++) {

Review Comment:
   Test is always true, because of [this condition](1).
   Test is always true, because of [this condition](2).



##########
iotdb-client/cli/src/main/java/org/apache/iotdb/cli/Cli.java:
##########
@@ -195,36 +247,87 @@ private static void executeSql(CliContext ctx) throws 
TException {
   }
 
   private static void receiveCommands(CliContext ctx) throws TException {
-    try (IoTDBConnection connection =
-        (IoTDBConnection)
-            DriverManager.getConnection(Config.IOTDB_URL_PREFIX + host + ":" + 
port + "/", info)) {
-      connection.setQueryTimeout(queryTimeout);
-      properties = connection.getServerProperties();
-      
AGGREGRATE_TIME_LIST.addAll(properties.getSupportedTimeAggregationOperations());
-      timestampPrecision = properties.getTimestampPrecision();
-
+    IoTDBConnection connection = null;
+    try {
+      connection = openConnection();
+      setupConnection(connection);
       echoStarting(ctx);
       displayLogo(ctx, properties.getLogo(), properties.getVersion(), 
properties.getBuildInfo());
       ctx.getPrinter().println(String.format("Successfully login at %s:%s", 
host, port));
       while (true) {
-        boolean readLine = readerReadLine(ctx, connection);
-        if (readLine) {
+        ReadLineResult result = readerReadLine(ctx, connection);
+        if (result.stop) {
           break;
         }
+        if (result.failedCommand != null) {
+          // Connection failed during processCommand; try to reconnect and 
retry the command.
+          closeConnectionQuietly(connection);
+          connection = null;
+          boolean reconnected = false;
+          for (int attempt = 1; attempt <= RECONNECT_RETRY_NUM; attempt++) {
+            if (attempt > 1) {
+              try {
+                Thread.sleep(RECONNECT_RETRY_INTERVAL_MS);
+              } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                ctx.getErr().printf("%s: Reconnection interrupted.%n", 
IOTDB_ERROR_PREFIX);
+                ctx.exit(CODE_ERROR);
+              }
+            }
+            try {
+              connection = openConnection();
+              setupConnection(connection);
+              ctx.getPrinter().println("Connection lost. Reconnected. Retrying 
command.");
+              processCommand(ctx, result.failedCommand, connection);
+              reconnected = true;
+              break;
+            } catch (SQLException e) {
+              if (isSessionOrStatementError(e)) {
+                // Reconnect succeeded but retry failed due to 
session/statement state; ask user to
+                // run the command again.
+                ctx.getPrinter()
+                    .println(
+                        "Reconnected, but the previous command could not be 
completed. Please run your command again.");
+                reconnected = true;
+                break;
+              }
+              if (attempt == RECONNECT_RETRY_NUM) {
+                ctx.getErr()
+                    .printf(
+                        "%s: Could not reconnect after %d attempts. Please 
check that the server is running and try again.%n",
+                        IOTDB_ERROR_PREFIX, RECONNECT_RETRY_NUM);
+                ctx.exit(CODE_ERROR);
+              }

Review Comment:
   In the reconnect loop, any SQLException thrown while retrying the failed 
command is treated as a reconnect failure. This can incorrectly trigger further 
reconnect attempts (and eventually exit with "Could not reconnect") for 
non-connection SQL errors (e.g., syntax/permission errors) that happen after 
the reconnect succeeds. Suggestion: only retry when `isConnectionRelated(e)` is 
true; otherwise report the SQL error from the retried command and continue the 
main loop (or stop) without attempting further reconnects.



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