This is an automated email from the ASF dual-hosted git repository.

rong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new d00c7665baa Subscription IT: execute jstack for potential stuck test 
cases (#12728)
d00c7665baa is described below

commit d00c7665baa3879b94c2e0cc4940c09cdf2d3237
Author: V_Galaxy <[email protected]>
AuthorDate: Mon Jun 17 10:29:31 2024 +0800

    Subscription IT: execute jstack for potential stuck test cases (#12728)
---
 .../iotdb/it/env/cluster/env/AbstractEnv.java      |  4 +-
 .../it/env/cluster/node/AbstractNodeWrapper.java   | 73 +++++++++++++++++++++-
 .../apache/iotdb/itbase/env/BaseNodeWrapper.java   | 16 ++++-
 .../it/dual/IoTDBSubscriptionConsumerGroupIT.java  | 10 +++
 4 files changed, 99 insertions(+), 4 deletions(-)

diff --git 
a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/env/AbstractEnv.java
 
b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/env/AbstractEnv.java
index 981d39dccca..949a7f21fd2 100644
--- 
a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/env/AbstractEnv.java
+++ 
b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/env/AbstractEnv.java
@@ -612,10 +612,10 @@ public abstract class AbstractEnv implements BaseEnv {
   @Override
   public void dumpTestJVMSnapshot() {
     for (ConfigNodeWrapper configNodeWrapper : configNodeWrapperList) {
-      configNodeWrapper.dumpJVMSnapshot(testMethodName);
+      configNodeWrapper.executeJstack(testMethodName);
     }
     for (DataNodeWrapper dataNodeWrapper : dataNodeWrapperList) {
-      dataNodeWrapper.dumpJVMSnapshot(testMethodName);
+      dataNodeWrapper.executeJstack(testMethodName);
     }
   }
 
diff --git 
a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/node/AbstractNodeWrapper.java
 
b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/node/AbstractNodeWrapper.java
index fa5447bc5b0..942b6a9e012 100644
--- 
a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/node/AbstractNodeWrapper.java
+++ 
b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/node/AbstractNodeWrapper.java
@@ -38,11 +38,14 @@ import javax.management.remote.JMXConnector;
 import javax.management.remote.JMXConnectorFactory;
 import javax.management.remote.JMXServiceURL;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.lang.management.ManagementFactory;
 import java.lang.management.MonitorInfo;
+import java.lang.management.RuntimeMXBean;
 import java.lang.management.ThreadInfo;
 import java.lang.management.ThreadMXBean;
 import java.net.MalformedURLException;
@@ -61,6 +64,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 import java.util.stream.Stream;
 
 import static 
org.apache.iotdb.it.env.cluster.ClusterConstant.CLUSTER_CONFIGURATIONS;
@@ -566,7 +570,6 @@ public abstract class AbstractNodeWrapper implements 
BaseNodeWrapper {
     return System.getProperty(USER_DIR) + File.separator + TARGET + 
File.separator + getId();
   }
 
-  @Override
   public void dumpJVMSnapshot(String testCaseName) {
     JMXServiceURL url;
     try {
@@ -675,4 +678,72 @@ public abstract class AbstractNodeWrapper implements 
BaseNodeWrapper {
   public abstract String getSystemPropertiesPath();
 
   protected abstract MppJVMConfig initVMConfig();
+
+  @Override
+  public void executeJstack() {
+    executeJstack(logger::info);
+  }
+
+  @Override
+  public void executeJstack(final String testCaseName) {
+    final String fileName =
+        getLogDirPath() + File.separator + testCaseName + "_" + getId() + 
"-threads.jstack";
+    try (final PrintWriter output = new PrintWriter(fileName)) {
+      executeJstack(output::println);
+    } catch (final IOException e) {
+      logger.warn("IOException occurred when executing Jstack for {}", 
this.getId(), e);
+    }
+    logger.info("Jstack execution output can be found at {}", fileName);
+  }
+
+  private void executeJstack(final Consumer<String> consumer) {
+    final long pid = this.getPid();
+    if (pid == -1) {
+      logger.warn("Failed to get pid for {} before executing Jstack", 
this.getId());
+      return;
+    }
+    final String command = "jstack -l " + pid;
+    logger.info("Executing command {} for {}", command, this.getId());
+    try {
+      final Process process = Runtime.getRuntime().exec(command);
+      try (final BufferedReader reader =
+          new BufferedReader(new InputStreamReader(process.getInputStream()))) 
{
+        String line;
+        while ((line = reader.readLine()) != null) {
+          consumer.accept(line);
+        }
+      }
+      final int exitCode = process.waitFor();
+      logger.info("Command {} exited with code {}", command, exitCode);
+    } catch (final IOException e) {
+      logger.warn("IOException occurred when executing Jstack for {}", 
this.getId(), e);
+    } catch (final InterruptedException e) {
+      Thread.currentThread().interrupt();
+      logger.warn("InterruptedException occurred when executing Jstack for 
{}", this.getId(), e);
+    }
+  }
+
+  /**
+   * @return The native process ID of the process, -1 if failure.
+   */
+  @Override
+  public long getPid() {
+    final JMXServiceURL url;
+    try {
+      url =
+          new JMXServiceURL(
+              
String.format("service:jmx:rmi:///jndi/rmi://127.0.0.1:%d/jmxrmi", jmxPort));
+    } catch (final MalformedURLException ignored) {
+      return -1;
+    }
+    try (final JMXConnector connector = JMXConnectorFactory.connect(url)) {
+      final MBeanServerConnection mbsc = connector.getMBeanServerConnection();
+      final RuntimeMXBean rmbean =
+          ManagementFactory.newPlatformMXBeanProxy(
+              mbsc, ManagementFactory.RUNTIME_MXBEAN_NAME, 
RuntimeMXBean.class);
+      return Long.parseLong(rmbean.getName().split("@")[0]);
+    } catch (final Throwable ignored) {
+      return -1;
+    }
+  }
 }
diff --git 
a/integration-test/src/main/java/org/apache/iotdb/itbase/env/BaseNodeWrapper.java
 
b/integration-test/src/main/java/org/apache/iotdb/itbase/env/BaseNodeWrapper.java
index cd0eb86bde1..b0ce0d4a221 100644
--- 
a/integration-test/src/main/java/org/apache/iotdb/itbase/env/BaseNodeWrapper.java
+++ 
b/integration-test/src/main/java/org/apache/iotdb/itbase/env/BaseNodeWrapper.java
@@ -45,5 +45,19 @@ public interface BaseNodeWrapper {
 
   String getIpAndPortString();
 
-  void dumpJVMSnapshot(String testCaseName);
+  /**
+   * Perform jstack on the process corresponding to the wrapper, and use 
logger to output the
+   * results.
+   */
+  void executeJstack();
+
+  /**
+   * Perform jstack on the process corresponding to the wrapper, and output 
the results to a file in
+   * the log directory.
+   *
+   * @param testCaseName the name of test case
+   */
+  void executeJstack(final String testCaseName);
+
+  long getPid();
 }
diff --git 
a/integration-test/src/test/java/org/apache/iotdb/subscription/it/dual/IoTDBSubscriptionConsumerGroupIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/subscription/it/dual/IoTDBSubscriptionConsumerGroupIT.java
index 650f861f498..e3325749250 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/subscription/it/dual/IoTDBSubscriptionConsumerGroupIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/subscription/it/dual/IoTDBSubscriptionConsumerGroupIT.java
@@ -1010,6 +1010,7 @@ public class IoTDBSubscriptionConsumerGroupIT extends 
AbstractSubscriptionDualIT
     }
 
     // Check data on receiver
+    final long[] currentTime = {System.currentTimeMillis()};
     try {
       try (final Connection connection = receiverEnv.getConnection();
           final Statement statement = connection.createStatement()) {
@@ -1020,6 +1021,15 @@ public class IoTDBSubscriptionConsumerGroupIT extends 
AbstractSubscriptionDualIT
                 LOGGER.info("detect receiver crashed, skipping this test...");
                 return;
               }
+              // potential stuck
+              if (System.currentTimeMillis() - currentTime[0] > 60_000L) {
+                for (final DataNodeWrapper wrapper : 
senderEnv.getDataNodeWrapperList()) {
+                  wrapper.executeJstack();
+                  // wrapper.executeJstack(String.format("%s_%s", 
testName.getMethodName(),
+                  // currentTime[0]));
+                }
+                currentTime[0] = System.currentTimeMillis();
+              }
               TestUtils.assertSingleResultSetEqual(
                   TestUtils.executeQueryWithRetry(statement, "select count(*) 
from root.**"),
                   expectedHeaderWithResult);

Reply via email to