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

amichair pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/aries-rsa.git


The following commit(s) were added to refs/heads/master by this push:
     new 31e2f603 ARIES-2239 Fix race condition when TcpConnectionManager 
resolves concurrent connections
31e2f603 is described below

commit 31e2f603bb6da48798c3d1edb573e6455cdc7312
Author: Amichai Rothman <[email protected]>
AuthorDate: Wed Jun 17 13:11:13 2026 +0300

    ARIES-2239 Fix race condition when TcpConnectionManager resolves concurrent 
connections
---
 .../rsa/discovery/tcp/TcpConnectionManager.java    | 62 +++++++++++++++++-----
 1 file changed, 48 insertions(+), 14 deletions(-)

diff --git 
a/discovery/tcp/src/main/java/org/apache/aries/rsa/discovery/tcp/TcpConnectionManager.java
 
b/discovery/tcp/src/main/java/org/apache/aries/rsa/discovery/tcp/TcpConnectionManager.java
index 4962a16c..399e2ed3 100644
--- 
a/discovery/tcp/src/main/java/org/apache/aries/rsa/discovery/tcp/TcpConnectionManager.java
+++ 
b/discovery/tcp/src/main/java/org/apache/aries/rsa/discovery/tcp/TcpConnectionManager.java
@@ -189,9 +189,10 @@ public class TcpConnectionManager implements 
EndpointEventListener {
         LOG.debug("received message {} on connection {}", message, conn);
         if (message instanceof HandshakeMessage) {
             HandshakeMessage h = (HandshakeMessage) message;
+            String peerUuid = h.getUuid();
             // update the peer data
             conn.setPeerAddress(h.getAddress());
-            conn.setPeerUuid(h.getUuid());
+            conn.setPeerUuid(peerUuid);
             // if gossip is enabled, try adding all of this peer's peers,
             // as well as the peer itself (in case it found us via its
             // own gossip, and we haven't met before)
@@ -200,21 +201,54 @@ public class TcpConnectionManager implements 
EndpointEventListener {
                 addPeers(Collections.singleton(h.getAddress()));
             }
             // if we already have another connection with this peer (e.g. 
reverse direction)
-            // then we keep the old one (which is already in use) and close 
the new one
-            boolean existing = connectionsByUuid.putIfAbsent(h.getUuid(), 
conn) != null;
+            // then we use the outbound connection from the peer with the 
higher uuid -
+            // this way both peers agree on which connection to use in case of 
a race.
+            // Once the decision is made about which connection to keep and 
which to close,
+            // there is still the question of which side performs the closing:
+            // there is a possible race condition where one peer receives the 
handshake
+            // and closes the connection before it even had a chance to send 
its handshake -
+            // so the other peer will never know that the connection is 
intentionally closed
+            // and not simply experiencing connection errors. If it is the 
outbound side, it
+            // will keep retrying to connect. to solve this, only the outbound 
originator is the
+            // one that closes the connection. The inbound side just avoids 
using it until then.
+            boolean existing = connectionsByUuid.putIfAbsent(peerUuid, conn) 
!= null;
             if (existing) {
-                // both sides can check if a connection between them already 
exists, but
-                // there is a possible race condition where one peer receives 
the handshake
-                // and closes the connection before it even had a chance to 
send its handshake -
-                // so the other peer will never know the connection is 
intentionally closed
-                // and not experiencing connection errors. If it is the 
outbound side, it will
-                // keep retrying to connect. to solve this, only the outbound 
peer is the one
-                // that closes the connection. The inbound side just doesn't 
use it until then.
-                if (conn.isOutbound()) {
-                    LOG.debug("connection already exists with peer {}, closing 
{}", h.getUuid(), conn);
-                    conn.close();
+                boolean winner = localUuid.compareTo(peerUuid) > 0; // we have 
higher uuid
+                TcpConnection prev = null;
+                if (winner) {
+                    if (conn.isOutbound()) {
+                        // new connection is our winner outbound -
+                        // use it instead of existing (and loser peer will 
close their outbound)
+                        prev = connectionsByUuid.put(peerUuid, conn);
+                        LOG.debug("connection already exists with peer {}, 
switching from {} to {}",
+                            peerUuid, prev, conn);
+                    } else {
+                        // new connection is the loser's outbound (our 
inbound) -
+                        // leave the existing winner outbound as-is
+                        LOG.debug("connection already exists with peer {}, 
ignoring {}",
+                            peerUuid, conn);
+                    }
+                } else {
+                    if (conn.isOutbound()) {
+                        // new connection is our loser outbound -
+                        // close it and leave the existing winner outbound 
as-is
+                        LOG.debug("connection already exists with peer {}, 
ignoring {} and closing it",
+                            peerUuid, conn);
+                        conn.close();
+                    } else {
+                        // new connection is the winner's outbound (our 
inbound) -
+                        // use it instead of existing and close our outbound
+                        prev = connectionsByUuid.put(peerUuid, conn);
+                        LOG.debug("connection already exists with peer {}, 
switching from {} to {} and closing",
+                            peerUuid, prev, conn);
+                        prev.close();
+                    }
+                }
+                // if we didn't swap connections, we already sent the known 
endpoints
+                // on the existing connection so no need to send them again
+                if (prev == null) {
+                    return;
                 }
-                return;
             }
             // send all of our known local endpoints to the new peer
             localEndpointManager.getEndpoints().forEach(

Reply via email to