2121
2222import org .apache .iotdb .common .rpc .thrift .TConsensusGroupType ;
2323import org .apache .iotdb .common .rpc .thrift .TEndPoint ;
24+ import org .apache .iotdb .common .rpc .thrift .TSStatus ;
2425import org .apache .iotdb .commons .client .ClientPoolFactory ;
2526import org .apache .iotdb .commons .client .IClientManager ;
2627import org .apache .iotdb .commons .client .exception .ClientManagerException ;
7980
8081public abstract class AbstractEnv implements BaseEnv {
8182 private static final Logger logger = IoTDBTestLogger .logger ;
83+ private static final int DEFAULT_CLUSTER_READY_RETRY_COUNT = 30 ;
84+ private static final String CLUSTER_READY_RETRY_COUNT_PROPERTY =
85+ "integrationTest.clusterReadyRetryCount" ;
8286
8387 private final Random rand = new Random ();
8488 protected List <ConfigNodeWrapper > configNodeWrapperList = Collections .emptyList ();
@@ -87,7 +91,7 @@ public abstract class AbstractEnv implements BaseEnv {
8791 protected String testMethodName = null ;
8892 protected int index = 0 ;
8993 protected long startTime ;
90- protected int retryCount = 30 ;
94+ protected int retryCount = getDefaultClusterReadyRetryCount () ;
9195 private IClientManager <TEndPoint , SyncConfigNodeIServiceClient > clientManager ;
9296 private List <String > configNodeKillPoints = new ArrayList <>();
9397 private List <String > dataNodeKillPoints = new ArrayList <>();
@@ -110,6 +114,12 @@ protected AbstractEnv(final long startTime) {
110114 this .clusterConfig = new MppClusterConfig ();
111115 }
112116
117+ private static int getDefaultClusterReadyRetryCount () {
118+ final int configuredRetryCount =
119+ Integer .getInteger (CLUSTER_READY_RETRY_COUNT_PROPERTY , DEFAULT_CLUSTER_READY_RETRY_COUNT );
120+ return configuredRetryCount > 0 ? configuredRetryCount : DEFAULT_CLUSTER_READY_RETRY_COUNT ;
121+ }
122+
113123 @ Override
114124 public ClusterConfig getConfig () {
115125 return clusterConfig ;
@@ -331,12 +341,14 @@ private Map<String, Integer> countNodeStatus(final Map<Integer, String> nodeStat
331341 }
332342
333343 public void checkNodeInStatus (int nodeId , NodeStatus expectation ) {
334- checkClusterStatus (nodeStatusMap -> expectation .getStatus ().equals (nodeStatusMap .get (nodeId )));
344+ checkClusterStatus (
345+ nodeStatusMap -> expectation .getStatus ().equals (nodeStatusMap .get (nodeId )), m -> true );
335346 }
336347
337348 public void checkClusterStatusWithoutUnknown () {
338349 checkClusterStatus (
339- nodeStatusMap -> nodeStatusMap .values ().stream ().noneMatch ("Unknown" ::equals ));
350+ nodeStatusMap -> nodeStatusMap .values ().stream ().noneMatch ("Unknown" ::equals ),
351+ processStatus -> processStatus .values ().stream ().noneMatch (i -> i != 0 ));
340352 testJDBCConnection ();
341353 }
342354
@@ -346,6 +358,10 @@ public void checkClusterStatusOneUnknownOtherRunning() {
346358 Map <String , Integer > count = countNodeStatus (nodeStatus );
347359 return count .getOrDefault ("Unknown" , 0 ) == 1
348360 && count .getOrDefault ("Running" , 0 ) == nodeStatus .size () - 1 ;
361+ },
362+ processStatus -> {
363+ long aliveProcessCount = processStatus .values ().stream ().filter (i -> i == 0 ).count ();
364+ return aliveProcessCount == processStatus .size () - 1 ;
349365 });
350366 testJDBCConnection ();
351367 }
@@ -357,38 +373,89 @@ public void checkClusterStatusOneUnknownOtherRunning() {
357373 * @param statusCheck the predicate to test the status of nodes
358374 */
359375 public void checkClusterStatus (final Predicate <Map <Integer , String >> statusCheck ) {
376+ checkClusterStatus (
377+ statusCheck , processStatus -> processStatus .values ().stream ().noneMatch (i -> i != 0 ));
378+ }
379+
380+ /**
381+ * check whether all nodes' status match the provided predicate with RPC. after retryCount times,
382+ * if the status of all nodes still not match the predicate, throw AssertionError.
383+ *
384+ * @param nodeStatusCheck the predicate to test the status of nodes
385+ * @param processStatusCheck the predicate to test the status of processes
386+ */
387+ public void checkClusterStatus (
388+ final Predicate <Map <Integer , String >> nodeStatusCheck ,
389+ final Predicate <Map <AbstractNodeWrapper , Integer >> processStatusCheck ) {
360390 logger .info ("Testing cluster environment..." );
361391 TShowClusterResp showClusterResp ;
362392 Exception lastException = null ;
363- boolean flag ;
393+ boolean passed ;
394+ boolean showClusterPassed = true ;
395+ boolean nodeSizePassed = true ;
396+ boolean nodeStatusPassed = true ;
397+ boolean processStatusPassed = true ;
398+ TSStatus showClusterStatus = null ;
399+ int actualNodeSize = 0 ;
400+ Map <Integer , String > lastNodeStatus = null ;
401+ Map <AbstractNodeWrapper , Integer > processStatusMap = new HashMap <>();
402+
364403 for (int i = 0 ; i < retryCount ; i ++) {
365404 try (final SyncConfigNodeIServiceClient client =
366405 (SyncConfigNodeIServiceClient ) getLeaderConfigNodeConnection ()) {
367- flag = true ;
406+ passed = true ;
407+ showClusterPassed = true ;
408+ nodeSizePassed = true ;
409+ nodeStatusPassed = true ;
410+ processStatusPassed = true ;
411+ processStatusMap .clear ();
412+
368413 showClusterResp = client .showCluster ();
414+ showClusterStatus = showClusterResp .getStatus ();
415+ actualNodeSize = showClusterResp .getNodeStatusSize ();
416+ lastNodeStatus = showClusterResp .getNodeStatus ();
369417
370418 // Check resp status
371419 if (showClusterResp .getStatus ().getCode () != TSStatusCode .SUCCESS_STATUS .getStatusCode ()) {
372- flag = false ;
420+ passed = false ;
421+ showClusterPassed = false ;
373422 }
374423
375424 // Check the number of nodes
376425 if (showClusterResp .getNodeStatus ().size ()
377426 != configNodeWrapperList .size ()
378427 + dataNodeWrapperList .size ()
379428 + aiNodeWrapperList .size ()) {
380- flag = false ;
429+ passed = false ;
430+ nodeSizePassed = false ;
381431 }
382432
383433 // Check the status of nodes
384- if (flag ) {
385- flag = statusCheck .test (showClusterResp .getNodeStatus ());
434+ if (passed ) {
435+ passed = nodeStatusCheck .test (showClusterResp .getNodeStatus ());
436+ if (!passed ) {
437+ nodeStatusPassed = false ;
438+ }
439+ }
440+
441+ collectProcessStatus (processStatusMap );
442+ processStatusPassed = processStatusCheck .test (processStatusMap );
443+ if (!processStatusPassed ) {
444+ passed = false ;
445+ handleProcessStatus (processStatusMap );
386446 }
387447
388- if (flag ) {
448+ if (passed ) {
389449 logger .info ("The cluster is now ready for testing!" );
390450 return ;
391451 }
452+ logger .info (
453+ "Retry {}: showClusterPassed={}, nodeSizePassed={}, nodeStatusPassed={}, processStatusPassed={}" ,
454+ i ,
455+ showClusterPassed ,
456+ nodeSizePassed ,
457+ nodeStatusPassed ,
458+ processStatusPassed );
392459 } catch (final Exception e ) {
393460 lastException = e ;
394461 }
@@ -405,8 +472,132 @@ public void checkClusterStatus(final Predicate<Map<Integer, String>> statusCheck
405472 lastException .getMessage (),
406473 lastException );
407474 }
475+ if (!showClusterPassed ) {
476+ logger .error ("Show cluster failed: {}" , showClusterStatus );
477+ }
478+ if (!nodeSizePassed ) {
479+ logger .error ("Only {} nodes detected" , actualNodeSize );
480+ }
481+ if (!nodeStatusPassed ) {
482+ logger .error ("Some node status incorrect: {}" , lastNodeStatus );
483+ }
484+ if (!processStatusPassed ) {
485+ logger .error ("Some process status incorrect: {}" , formatProcessStatus (processStatusMap ));
486+ }
487+
488+ dumpTestJVMSnapshotQuietly ("cluster status check failed" );
408489 throw new AssertionError (
409- String .format ("After %d times retry, the cluster can't work!" , retryCount ));
490+ buildClusterStatusFailureMessage (
491+ showClusterPassed ,
492+ nodeSizePassed ,
493+ nodeStatusPassed ,
494+ processStatusPassed ,
495+ showClusterStatus ,
496+ actualNodeSize ,
497+ lastNodeStatus ,
498+ processStatusMap ,
499+ lastException ));
500+ }
501+
502+ private void collectProcessStatus (final Map <AbstractNodeWrapper , Integer > processStatusMap )
503+ throws InterruptedException {
504+ final List <AbstractNodeWrapper > allNodeWrappers = new ArrayList <>();
505+ allNodeWrappers .addAll (configNodeWrapperList );
506+ allNodeWrappers .addAll (dataNodeWrapperList );
507+ allNodeWrappers .addAll (aiNodeWrapperList );
508+ for (final AbstractNodeWrapper nodeWrapper : allNodeWrappers ) {
509+ final Process process = nodeWrapper .getInstance ();
510+ if (process == null ) {
511+ processStatusMap .put (nodeWrapper , -1 );
512+ } else {
513+ processStatusMap .put (nodeWrapper , process .isAlive () ? 0 : process .waitFor ());
514+ }
515+ }
516+ }
517+
518+ private String buildClusterStatusFailureMessage (
519+ final boolean showClusterPassed ,
520+ final boolean nodeSizePassed ,
521+ final boolean nodeStatusPassed ,
522+ final boolean processStatusPassed ,
523+ final TSStatus showClusterStatus ,
524+ final int actualNodeSize ,
525+ final Map <Integer , String > lastNodeStatus ,
526+ final Map <AbstractNodeWrapper , Integer > processStatusMap ,
527+ final Exception lastException ) {
528+ final StringBuilder builder =
529+ new StringBuilder (
530+ String .format ("After %d times retry, the cluster status check failed" , retryCount ));
531+ builder
532+ .append (": showClusterPassed=" )
533+ .append (showClusterPassed )
534+ .append (", nodeSizePassed=" )
535+ .append (nodeSizePassed )
536+ .append (", nodeStatusPassed=" )
537+ .append (nodeStatusPassed )
538+ .append (", processStatusPassed=" )
539+ .append (processStatusPassed )
540+ .append (", expectedNodeSize=" )
541+ .append (
542+ configNodeWrapperList .size () + dataNodeWrapperList .size () + aiNodeWrapperList .size ())
543+ .append (", actualNodeSize=" )
544+ .append (actualNodeSize );
545+ if (showClusterStatus != null ) {
546+ builder .append (", showClusterStatus=" ).append (showClusterStatus );
547+ }
548+ if (lastNodeStatus != null ) {
549+ builder .append (", lastNodeStatus=" ).append (lastNodeStatus );
550+ }
551+ if (!processStatusMap .isEmpty ()) {
552+ builder .append (", processStatus=" ).append (formatProcessStatus (processStatusMap ));
553+ }
554+ if (lastException != null ) {
555+ builder
556+ .append (", lastException=" )
557+ .append (lastException .getClass ().getName ())
558+ .append (": " )
559+ .append (lastException .getMessage ());
560+ }
561+ builder .append (", logDirs=" ).append (getClusterLogDirs ());
562+ return builder .toString ();
563+ }
564+
565+ private Map <String , Integer > formatProcessStatus (
566+ final Map <AbstractNodeWrapper , Integer > processStatusMap ) {
567+ final Map <String , Integer > result = new LinkedHashMap <>();
568+ processStatusMap .forEach (
569+ (nodeWrapper , statusCode ) -> result .put (nodeWrapper .getId (), statusCode ));
570+ return result ;
571+ }
572+
573+ private List <String > getClusterLogDirs () {
574+ final List <AbstractNodeWrapper > allNodeWrappers = new ArrayList <>();
575+ allNodeWrappers .addAll (configNodeWrapperList );
576+ allNodeWrappers .addAll (dataNodeWrapperList );
577+ allNodeWrappers .addAll (aiNodeWrapperList );
578+ return allNodeWrappers .stream ()
579+ .map (AbstractNodeWrapper ::getLogDirPath )
580+ .distinct ()
581+ .collect (Collectors .toList ());
582+ }
583+
584+ private void dumpTestJVMSnapshotQuietly (final String reason ) {
585+ try {
586+ logger .info ("Dumping test JVM snapshots because {}." , reason );
587+ dumpTestJVMSnapshot ();
588+ } catch (final Exception e ) {
589+ logger .warn ("Failed to dump test JVM snapshots after {}" , reason , e );
590+ }
591+ }
592+
593+ private void handleProcessStatus (final Map <AbstractNodeWrapper , Integer > processStatusMap ) {
594+ for (final Map .Entry <AbstractNodeWrapper , Integer > entry : processStatusMap .entrySet ()) {
595+ final Integer statusCode = entry .getValue ();
596+ final AbstractNodeWrapper nodeWrapper = entry .getKey ();
597+ if (statusCode != 0 ) {
598+ logger .info ("Node {} is not running due to {}" , nodeWrapper .getId (), statusCode );
599+ }
600+ }
410601 }
411602
412603 @ Override
@@ -698,6 +889,7 @@ protected void testJDBCConnection() {
698889 .collect (Collectors .toList ());
699890 final RequestDelegate <Void > testDelegate =
700891 new ParallelRequestDelegate <>(endpoints , NODE_START_TIMEOUT , this );
892+ final Map <String , String > lastConnectionFailures = Collections .synchronizedMap (new HashMap <>());
701893 for (final DataNodeWrapper dataNode : dataNodeWrapperList ) {
702894 final String dataNodeEndpoint = dataNode .getIpAndPortString ();
703895 testDelegate .addRequest (
@@ -716,6 +908,8 @@ protected void testJDBCConnection() {
716908 return null ;
717909 } catch (final Exception e ) {
718910 lastException = e ;
911+ lastConnectionFailures .put (
912+ dataNodeEndpoint , e .getClass ().getName () + ": " + e .getMessage ());
719913 TimeUnit .SECONDS .sleep (1L );
720914 }
721915 }
@@ -729,8 +923,11 @@ protected void testJDBCConnection() {
729923 testDelegate .requestAll ();
730924 } catch (final Exception e ) {
731925 logger .error ("exception in test Cluster with RPC, message: {}" , e .getMessage (), e );
926+ dumpTestJVMSnapshotQuietly ("JDBC connection check failed" );
732927 throw new AssertionError (
733- String .format ("After %d times retry, the cluster can't work!" , retryCount ));
928+ String .format (
929+ "After %d times retry, JDBC connections to DataNodes are not ready. endpoints=%s, lastConnectionFailures=%s, logDirs=%s" ,
930+ retryCount , endpoints , lastConnectionFailures , getClusterLogDirs ()));
734931 }
735932 }
736933
0 commit comments