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

exceptionfactory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new 2f74b170765 NIFI-15903 Improved flaky tests ClusteredConnectorIT and 
ContentClaimTruncationIT (#11203)
2f74b170765 is described below

commit 2f74b1707652a5dfb615cb6997a3d93deb81db2c
Author: Pierre Villard <[email protected]>
AuthorDate: Tue May 5 15:47:44 2026 +0200

    NIFI-15903 Improved flaky tests ClusteredConnectorIT and 
ContentClaimTruncationIT (#11203)
    
    - Added test method for robust cluster node removal based on coordinator 
reported status
    
    Signed-off-by: David Handermann <[email protected]>
---
 .../apache/nifi/tests/system/NiFiClientUtil.java   | 17 +++++++++++++++
 .../org/apache/nifi/tests/system/NiFiSystemIT.java | 24 ++++++++++++++++++++++
 ...JoinClusterWithMissingConnectionWithDataIT.java | 12 +----------
 .../system/connectors/ClusteredConnectorIT.java    |  7 ++-----
 4 files changed, 44 insertions(+), 16 deletions(-)

diff --git 
a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiClientUtil.java
 
b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiClientUtil.java
index 17de5d0ce14..2de5c0f915e 100644
--- 
a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiClientUtil.java
+++ 
b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiClientUtil.java
@@ -1171,6 +1171,23 @@ public class NiFiClientUtil {
             final Integer terminatedThreadCount = 
snapshotDto.getTerminatedThreadCount();
 
             if ("RUNNING".equals(expectedState) || (activeThreadCount == 0 && 
terminatedThreadCount == 0)) {
+                // The logical state masks the framework's physical STOPPING 
state as STOPPED. The framework's
+                // verifyCanStart check evaluates the physical state, so when 
the caller is waiting for STOPPED
+                // we additionally require the physical state to have settled 
to STOPPED or DISABLED. Without
+                // this, a tight loop of runProcessorOnce + 
waitForStoppedProcessor can race against an
+                // in-flight stop transition and the next start request fails 
with "cannot be started because
+                // it is not stopped. Current state is STOPPING".
+                if ("STOPPED".equalsIgnoreCase(expectedState)) {
+                    final String physicalState = 
entity.getComponent().getPhysicalState();
+                    final boolean physicalStateSettled = physicalState == null
+                            || "STOPPED".equalsIgnoreCase(physicalState)
+                            || "DISABLED".equalsIgnoreCase(physicalState);
+                    if (!physicalStateSettled) {
+                        Thread.sleep(10L);
+                        continue;
+                    }
+                }
+
                 logger.info("Processor {} is now in desired state of {} with 
{} active threads and {} terminated threads",
                     processorId, expectedState, activeThreadCount, 
terminatedThreadCount);
                 return;
diff --git 
a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiSystemIT.java
 
b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiSystemIT.java
index fab72e3cd50..fae7e7bd76e 100644
--- 
a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiSystemIT.java
+++ 
b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiSystemIT.java
@@ -703,6 +703,30 @@ public abstract class NiFiSystemIT implements 
NiFiInstanceProvider {
         
getNifiClient().getControllerClient().connectNode(nodeEntity.getNode().getNodeId(),
 nodeEntity);
     }
 
+    /**
+     * Removes a node from the cluster and waits until the cluster coordinator 
no longer reports the node.
+     * Mutating cluster requests (such as deleting a component) reject while a 
non-CONNECTED node is still
+     * known to the coordinator, so callers that need to issue follow-up 
mutating requests must wait for
+     * the removal to be reflected in the cluster state rather than relying on 
the synchronous response of
+     * {@code deleteNode} alone.
+     *
+     * @param nodeIndex the 1-based index of the node
+     */
+    protected void removeNodeFromCluster(final int nodeIndex) throws 
NiFiClientException, IOException, InterruptedException {
+        final NodeEntity nodeEntity = getNodeEntity(nodeIndex);
+        final int expectedPort = getClientApiPort() + nodeIndex - 1;
+        
getNifiClient().getControllerClient().deleteNode(nodeEntity.getNode().getNodeId());
+
+        waitFor(() -> {
+            try {
+                return 
getNifiClient().getControllerClient().getNodes().getCluster().getNodes().stream()
+                        .noneMatch(dto -> dto.getApiPort() == expectedPort);
+            } catch (final Exception e) {
+                return false;
+            }
+        });
+    }
+
     protected NodeEntity getNodeEntity(final int nodeIndex) throws 
NiFiClientException, IOException {
         final ClusterEntity clusterEntity = 
getNifiClient().getControllerClient().getNodes();
         final int expectedPort = getClientApiPort() + nodeIndex - 1;
diff --git 
a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/clustering/JoinClusterWithMissingConnectionWithDataIT.java
 
b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/clustering/JoinClusterWithMissingConnectionWithDataIT.java
index 55127a49e36..d6bb11603d1 100644
--- 
a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/clustering/JoinClusterWithMissingConnectionWithDataIT.java
+++ 
b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/clustering/JoinClusterWithMissingConnectionWithDataIT.java
@@ -99,8 +99,7 @@ public class JoinClusterWithMissingConnectionWithDataIT 
extends NiFiSystemIT {
         node2.stop();
 
         // Remove node from the cluster
-        getNifiClient().getControllerClient().deleteNode(node2Dto.getNodeId());
-        waitFor(() -> isNodeRemoved(5672));
+        removeNodeFromCluster(2);
 
         // Drop the data in the queue and delete the queue.
         getClientUtil().emptyQueue(CONNECTION_UUID);
@@ -113,15 +112,6 @@ public class JoinClusterWithMissingConnectionWithDataIT 
extends NiFiSystemIT {
         waitFor(() -> isNodeDisconnected(5672));
     }
 
-    private boolean isNodeRemoved(final int apiPort) {
-        try {
-            return 
getNifiClient().getControllerClient().getNodes().getCluster().getNodes().stream()
-                .noneMatch(dto -> dto.getApiPort() == apiPort);
-        } catch (Exception e) {
-            return false;
-        }
-    }
-
     private boolean isNodeDisconnected(final int apiPort) {
         try {
             final NodeDTO nodeDto = 
getNifiClient().getControllerClient().getNodes().getCluster().getNodes().stream()
diff --git 
a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/connectors/ClusteredConnectorIT.java
 
b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/connectors/ClusteredConnectorIT.java
index cc22e9fa271..d377b866362 100644
--- 
a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/connectors/ClusteredConnectorIT.java
+++ 
b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/connectors/ClusteredConnectorIT.java
@@ -24,7 +24,6 @@ import org.apache.nifi.toolkit.client.NiFiClientException;
 import org.apache.nifi.web.api.dto.ConnectorConfigurationDTO;
 import org.apache.nifi.web.api.dto.ConnectorValueReferenceDTO;
 import org.apache.nifi.web.api.entity.ConnectorEntity;
-import org.apache.nifi.web.api.entity.NodeEntity;
 import org.junit.jupiter.api.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -130,8 +129,7 @@ public class ClusteredConnectorIT extends ConnectorCrudIT {
         final ConnectorClient connectorClient = 
getNifiClient().getConnectorClient();
         assertThrows(NiFiClientException.class, () -> 
connectorClient.deleteConnector(connector));
 
-        final NodeEntity node2Entity = getNodeEntity(2);
-        
getNifiClient().getControllerClient().deleteNode(node2Entity.getNode().getNodeId());
+        removeNodeFromCluster(2);
 
         // Should now be able to delete connector
         connectorClient.deleteConnector(connector);
@@ -172,8 +170,7 @@ public class ClusteredConnectorIT extends ConnectorCrudIT {
         assertThrows(NiFiClientException.class, () -> 
connectorClient.deleteConnector(connector));
 
         // Remove node 2 from cluster.
-        final NodeEntity node2Entity = getNodeEntity(2);
-        
getNifiClient().getControllerClient().deleteNode(node2Entity.getNode().getNodeId());
+        removeNodeFromCluster(2);
 
         // We cannot delete the connector directly because it has data queued. 
Stop Node 1, delete the flow.json.gz file, and restart Node 1.
         getNiFiInstance().getNodeInstance(1).stop();

Reply via email to