@@ -354,20 +354,34 @@ public void testClusterStabilityWhenDisconnectDuringSlowNodeLeftTask() throws Ex
354354 @ RequiresTestIndexStoreListener
355355 public void testClusterStabilityWhenClusterStatePublicationLagsOnShardCleanup () throws Exception {
356356 additionalSetupForLagDuringDataMigration ();
357+ Map <ClusterApplierService , ClusterStateListener > blueNodeListeners = new HashMap <>();
357358 try {
358359 TestIndexStoreListener .DELAY_SHARD_ASSIGNMENT = true ;
360+
361+ // Also add delay listeners on blue nodes (source nodes) to ensure publication lag
362+ for (String nodeName : internalCluster ().getNodeNames ()) {
363+ Settings nodeSettings = internalCluster ().getInstance (Settings .class , nodeName );
364+ if ("blue" .equals (nodeSettings .get ("node.attr.color" )) && nodeSettings .getAsBoolean ("node.data" , true )) {
365+ ClusterApplierService applierService = internalCluster ().getInstance (ClusterService .class , nodeName )
366+ .getClusterApplierService ();
367+ ClusterStateListener listener = createDelayListener (applierService );
368+ blueNodeListeners .put (applierService , listener );
369+ applierService .addListener (listener );
370+ }
371+ }
372+
359373 logger .info ("Moving all shards to red nodes" );
360374 client ().admin ()
361375 .indices ()
362376 .prepareUpdateSettings ("*" )
363377 .setSettings (Settings .builder ().put ("index.routing.allocation.include.color" , "red" ))
364378 .get ();
365379 validateNodeDropDueToPublicationLag ();
366- TestIndexStoreListener .DELAY_SHARD_ASSIGNMENT = false ;
367- validateClusterRecovery ();
368380 } finally {
369381 TestIndexStoreListener .DELAY_SHARD_ASSIGNMENT = false ;
382+ blueNodeListeners .forEach (ClusterApplierService ::removeListener );
370383 }
384+ validateClusterRecovery ();
371385 }
372386
373387 public void testClusterStabilityWhenClusterStatePublicationLagsWithLongRunningListenerOnApplierThread () throws Exception {
@@ -448,20 +462,25 @@ private void additionalSetupForLagDuringDataMigration() {
448462 testLogsAppender .clearCapturedLogs ();
449463 }
450464
451- private void validateNodeDropDueToPublicationLag () {
452- ClusterHealthResponse clusterHealthResponse = internalCluster ().client ()
453- .admin ()
454- .cluster ()
455- .prepareHealth ()
456- .setWaitForNodes ("<9" )
457- .setTimeout (TimeValue .timeValueSeconds (120 ))
458- .get ();
459- logger .info ("Cluster health {}" , clusterHealthResponse );
460- assertFalse ("Cluster didn't have a node drop" , clusterHealthResponse .isTimedOut ());
461- boolean logFound ;
462- logFound = testLogsAppender .waitForLog ("Sleeping for 30 seconds" , 30 , TimeUnit .SECONDS );
463- assertTrue ("Expected log for delay in shard cleanup was not found within the timeout period" , logFound );
464- logFound = testLogsAppender .waitForLog (
465+ private void validateNodeDropDueToPublicationLag () throws Exception {
466+ // Wait for the delay log to appear first, confirming the delay mechanism is active
467+ boolean delayLogFound = testLogsAppender .waitForLog ("Sleeping for 30 seconds" , 60 , TimeUnit .SECONDS );
468+ assertTrue ("Expected log for delay in shard cleanup was not found within the timeout period" , delayLogFound );
469+
470+ // Use assertBusy to wait for node drop with retries
471+ assertBusy (() -> {
472+ ClusterHealthResponse clusterHealthResponse = internalCluster ().client ()
473+ .admin ()
474+ .cluster ()
475+ .prepareHealth ()
476+ .setWaitForNodes ("<9" )
477+ .setTimeout (TimeValue .timeValueSeconds (5 ))
478+ .get ();
479+ assertFalse ("Cluster didn't have a node drop yet" , clusterHealthResponse .isTimedOut ());
480+ }, 120 , TimeUnit .SECONDS );
481+
482+ logger .info ("Node drop detected, validating logs" );
483+ boolean logFound = testLogsAppender .waitForLog (
465484 "Tasks batched with key: org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor" ,
466485 30 ,
467486 TimeUnit .SECONDS
0 commit comments