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

ptupitsyn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 85da7c71ea7 IGNITE-27521 Java client: handle multiple endpoints for 
the same node (#7745)
85da7c71ea7 is described below

commit 85da7c71ea734ed43c9756fe5bfd8622c8e20251
Author: Pavel Tupitsyn <[email protected]>
AuthorDate: Wed Mar 11 14:20:11 2026 +0100

    IGNITE-27521 Java client: handle multiple endpoints for the same node 
(#7745)
---
 .../ignite/internal/client/ReliableChannel.java    | 20 +++++++++-
 .../ignite/client/ClientDnsDiscoveryTest.java      | 43 +++++++++++++++++++++-
 2 files changed, 61 insertions(+), 2 deletions(-)

diff --git 
a/modules/client/src/main/java/org/apache/ignite/internal/client/ReliableChannel.java
 
b/modules/client/src/main/java/org/apache/ignite/internal/client/ReliableChannel.java
index 9a6feff1995..f5774ea0b03 100644
--- 
a/modules/client/src/main/java/org/apache/ignite/internal/client/ReliableChannel.java
+++ 
b/modules/client/src/main/java/org/apache/ignite/internal/client/ReliableChannel.java
@@ -229,8 +229,14 @@ public final class ReliableChannel implements 
AutoCloseable {
      */
     public List<ClusterNode> connections() {
         List<ClusterNode> res = new ArrayList<>(channels.size());
+        Set<ClientChannelHolder> set = new HashSet<>();
+
+        for (var holder : channels) {
+            if (!set.add(holder)) {
+                // Duplicate address in config.
+                continue;
+            }
 
-        for (var holder : nodeChannelsByName.values()) {
             var chFut = holder.chFut;
 
             if (chFut != null) {
@@ -961,6 +967,18 @@ public final class ReliableChannel implements 
AutoCloseable {
 
                     ClusterNode newNode = ch.protocolContext().clusterNode();
 
+                    // Check if another endpoint already connected to this 
node.
+                    ClientChannelHolder existingHolder = 
nodeChannelsByName.get(newNode.name());
+                    if (existingHolder != null && existingHolder != this) {
+                        log.warn("Multiple distinct endpoints resolve to the 
same server node [nodeName={}, nodeId={}, "
+                                + "existingEndpoint={}, newEndpoint={}]. This 
represents a misconfiguration. "
+                                + "Both connections will remain active to 
avoid disrupting ongoing operations.",
+                                newNode.name(),
+                                newNode.id(),
+                                existingHolder.chCfg.getAddress(),
+                                chCfg.getAddress());
+                    }
+
                     // There could be multiple holders map to the same 
serverNodeId if user provide the same
                     // address multiple times in configuration.
                     nodeChannelsByName.put(newNode.name(), this);
diff --git 
a/modules/client/src/test/java/org/apache/ignite/client/ClientDnsDiscoveryTest.java
 
b/modules/client/src/test/java/org/apache/ignite/client/ClientDnsDiscoveryTest.java
index 07101010a2c..eb02a0fc835 100644
--- 
a/modules/client/src/test/java/org/apache/ignite/client/ClientDnsDiscoveryTest.java
+++ 
b/modules/client/src/test/java/org/apache/ignite/client/ClientDnsDiscoveryTest.java
@@ -37,7 +37,9 @@ import org.apache.ignite.internal.client.ReliableChannel;
 import org.apache.ignite.internal.client.TcpIgniteClient;
 import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
 import org.apache.ignite.internal.testframework.IgniteTestUtils;
+import org.apache.ignite.lang.LoggerFactory;
 import org.apache.ignite.network.ClusterNode;
+import org.jetbrains.annotations.Nullable;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
@@ -234,10 +236,49 @@ class ClientDnsDiscoveryTest extends 
BaseIgniteAbstractTest {
         }
     }
 
+    @Test
+    void testMultipleEndpointsSameNodeLogsWarning() throws 
InterruptedException {
+        // Two distinct IPs that both resolve to the same node (server3).
+        AtomicReference<String[]> resolvedAddressesRef = new 
AtomicReference<>(new String[]{loopbackAddress, hostAddress});
+        TestLoggerFactory loggerFactory = new TestLoggerFactory("test-client");
+        String[] addresses = {"my-cluster:" + server3.port()};
+
+        var cfg = getClientConfiguration(addresses, 0L, resolvedAddressesRef, 
loggerFactory);
+
+        List<?> channelHolders;
+
+        try (var client = TcpIgniteClient.startAsync(cfg).join()) {
+            assertDoesNotThrow(() -> client.tables().tables());
+            loggerFactory.waitForLogMatches(".*Multiple distinct endpoints 
resolve to the same server node.*", 5000);
+
+            List<ClusterNode> connections = client.connections();
+            assertEquals(2, connections.size());
+            assertEquals("server3", connections.get(0).name());
+            assertEquals("server3", connections.get(1).name());
+
+            channelHolders = IgniteTestUtils.getFieldValue(((TcpIgniteClient) 
client).channel(), "channels");
+            assertEquals(2, channelHolders.size());
+        }
+
+        for (Object holder : channelHolders) {
+            boolean isClosed = IgniteTestUtils.getFieldValue(holder, "close");
+            assertTrue(isClosed, "Channel holder should be closed after client 
close");
+        }
+    }
+
     private static IgniteClientConfigurationImpl getClientConfiguration(
             String[] addresses,
             long backgroundReResolveAddressesInterval,
             AtomicReference<String[]> resolvedAddressesRef
+    ) {
+        return getClientConfiguration(addresses, 
backgroundReResolveAddressesInterval, resolvedAddressesRef, null);
+    }
+
+    private static IgniteClientConfigurationImpl getClientConfiguration(
+            String[] addresses,
+            long backgroundReResolveAddressesInterval,
+            AtomicReference<String[]> resolvedAddressesRef,
+            @Nullable LoggerFactory loggerFactory
     ) {
         InetAddressResolver addressResolver = (host, port) -> {
             if ("my-cluster".equals(host)) {
@@ -258,7 +299,7 @@ class ClientDnsDiscoveryTest extends BaseIgniteAbstractTest 
{
                 50,
                 50,
                 new RetryLimitPolicy(),
-                null,
+                loggerFactory,
                 null,
                 false,
                 null,

Reply via email to