Sönke Liebau created NIFI-16547:
-----------------------------------

             Summary: Cluster protocol sockets are created without a connect 
timeout
                 Key: NIFI-16547
                 URL: https://issues.apache.org/jira/browse/NIFI-16547
             Project: Apache NiFi
          Issue Type: Bug
          Components: Core Framework
    Affects Versions: 2.7.2
         Environment: Observed with NiFi running on Kubernetes, affects any 
environment though.
            Reporter: Sönke Liebau
            Assignee: Sönke Liebau


h3. Description 

The code in `SocketUtils.createSocket` creates new sockets with `new 
Socket(host, port)` which tries connecting the socket to the target host 
immediately. 

The problem is, that this initial connection is not governed by the timeouts 
configurable via `nifi.cluster.node.read.timeout` or 
`nifi.cluster.node.connection.timeout`. Those are only applied later. The 
timeout for this initial connection is controlled by the kernel's SYN timeout, 
which is about 130 seconds with Linux defaults. This is not really relevant for 
any connection attempts that are actively rejected, but if packets are silently 
DROPed by a firewall or something like that along the way, this will "hang" for 
about 2 minutes.

This can be problematic in places, where this is called in a synchroneous 
context and blocks subsequent code. One such example is 
`notifyNodeStatusChange` which is called from the HeartbeatMonitor and fans out 
to all nodes. If one of those connects hangs, it blocks the heartbeat monitor 
for 120 seconds. While it waits, the coordinator processes no heartbeats at 
all, so nodes that are joining or rejoining stay CONNECTING:

On the client side of things, the same SocketUtils is used for heartbeats and 
connection requests. A node whose heartbeat goes to an address that drops 
packets blocks its heartbeat thread for the full connect timeout and gets 
disconnected for lack of heartbeat, although it is healthy.

We saw this create issues on NiFi clusters running on Kubernetes during rolling 
restarts. Nodes address each other by pod DNS name, and after a pod has exited 
its old IP can still be used for a while (DNS and JVM caching, or a name that 
was resolved just before the pod went away). Depending on the CNI 
implementation, a connect to the IP of an exited pod is silently dropped 
instead of rejected, so it hangs until the kernel gives up.

h3. Symptoms
We saw several issues over time during rolling restarts, that we think might be 
tied to this issue. 
- Nodes unable to rejoin for a long time.
- API returning 409 because coordinator is not CONNECTED
- Healthy nodes being disconnected

h3. Suggested fix
The fix should be fairly simple, replace the `new` calls with creating an 
unconnected socket and then calling connect(new InetSocketAddress(host, port), 
timeout), with nifi.cluster.node.connection.timeout as the timeout. I think 
there may be some Exception handling to tweak, but that should do away with the 
super long timeouts.

I ran a test with an image with that fix vs a vanilla image and had Claude 
compare behavior across a few rolling restart on K8s for both. Results are 
shown below - but this should not be treated as gospel, rather indications.

|| ||Stock||Fixed||
|Rollout duration|5:21|3:42|
|Restart until API answers 200 on all nodes again|5:23 (322 s)|3:33 (212 s)|
|Kill → that node back (CONNECTED + API 200), n2 / n1 / n0|302 / 255 / 105 
s|109 / 88 / 50 s|
|Running nodes disconnected for lack of heartbeat|3|0|
|NiFi shutdown time, n2 / n1 / n0|0 / 93 / 57 s|0 / 0 / 0 s|
|Time other, running nodes refused the API with 409|~95 s each on n1 and n2|~12 
s, only on the node rejoining|
|Longest heartbeat-monitor stall|5 s|5 s|

What changed with the fix:
 * Running nodes keep working. On stock, every coordinator handover got running 
nodes disconnected, and the API returned 409 on all three nodes for two windows 
(about 96 s and 74 s). In those windows even reading the cluster state and 
processor state failed everywhere. With the fix there were no such disconnects, 
and while one node restarted the other two answered normally.
 * Shutdown takes 0 s. On stock, shutdowns waited 57–93 s for a heartbeat stuck 
on the old coordinator's address. Faster shutdowns are a big part of why the 
whole rollout got 1:40 shorter.





--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to