IGNITE-17414 Fix node is trying to resolve its hostname if IP is configured with IGNITE_LOCAL_HOST (#10169)

diff --git a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
index 45de839..3c61822 100644
--- a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
+++ b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
@@ -43,6 +43,7 @@
 import org.apache.ignite.internal.processors.query.schema.SchemaIndexCachePartitionWorker;
 import org.apache.ignite.internal.processors.rest.GridRestCommand;
 import org.apache.ignite.internal.util.GridLogThrottle;
+import org.apache.ignite.internal.util.IgniteUtils;
 import org.apache.ignite.lang.IgniteExperimental;
 import org.apache.ignite.mxbean.MetricsMxBean;
 import org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi;
@@ -1012,6 +1013,17 @@
         "in configuration", type = String.class)
     public static final String IGNITE_OVERRIDE_CONSISTENT_ID = "IGNITE_OVERRIDE_CONSISTENT_ID";
 
+    /**
+     * System property to ignore reading hostname of the local address.
+     * <p>
+     * If set to {@code true} and {@link #IGNITE_LOCAL_HOST} is defined then
+     * {@link IgniteUtils#resolveLocalAddresses} will not add hostname of local address to the list of node's addresses.
+     * Defaults to {@code true}.
+     */
+    @SystemProperty(value = "Ignores local address's hostname if IGNITE_LOCAL_HOST is defined "
+        + "when resolving local node's addresses", defaults = "true")
+    public static final String IGNITE_IGNORE_LOCAL_HOST_NAME = "IGNITE_IGNORE_LOCAL_HOST_NAME";
+
     /** */
     @SystemProperty(value = "IO balance period in milliseconds", type = Long.class,
         defaults = "" + DFLT_IO_BALANCE_PERIOD)
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
index a29516d..ec87643 100755
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
@@ -140,6 +140,7 @@
 import java.util.concurrent.Future;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.Condition;
@@ -248,6 +249,7 @@
 import org.apache.ignite.internal.util.typedef.X;
 import org.apache.ignite.internal.util.typedef.internal.A;
 import org.apache.ignite.internal.util.typedef.internal.LT;
+import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.SB;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.internal.util.worker.GridWorker;
@@ -290,6 +292,7 @@
 import static java.util.Objects.isNull;
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_DISABLE_HOSTNAME_VERIFIER;
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_HOME;
+import static org.apache.ignite.IgniteSystemProperties.IGNITE_IGNORE_LOCAL_HOST_NAME;
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_LOCAL_HOST;
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_MBEAN_APPEND_CLASS_LOADER_ID;
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_MBEAN_APPEND_JVM_ID;
@@ -299,6 +302,7 @@
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_SSH_USER_NAME;
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_SUCCESS_FILE;
 import static org.apache.ignite.IgniteSystemProperties.getBoolean;
+import static org.apache.ignite.IgniteSystemProperties.getString;
 import static org.apache.ignite.events.EventType.EVTS_ALL;
 import static org.apache.ignite.events.EventType.EVTS_ALL_MINUS_METRIC_UPDATE;
 import static org.apache.ignite.events.EventType.EVT_NODE_METRICS_UPDATED;
@@ -318,6 +322,9 @@
  */
 @SuppressWarnings({"UnusedReturnValue"})
 public abstract class IgniteUtils {
+    /** Logger. */
+    private static final Logger log = Logger.getLogger(IgniteUtils.class.getName());
+
     /** */
     public static final long KB = 1024L;
 
@@ -2277,12 +2284,20 @@
      */
     private static void addresses(InetAddress addr, Collection<String> addrs, Collection<String> hostNames,
         boolean allHostNames) {
-        String hostName = addr.getHostName();
-
         String ipAddr = addr.getHostAddress();
 
         addrs.add(ipAddr);
 
+        boolean ignoreLocalHostName = getBoolean(IGNITE_IGNORE_LOCAL_HOST_NAME, true);
+
+        String userDefinedLocalHost = getString(IGNITE_LOCAL_HOST);
+
+        // If IGNITE_LOCAL_HOST is defined and IGNITE_IGNORE_LOCAL_HOST_NAME is not false, then ignore local address's hostname
+        if (!F.isEmpty(userDefinedLocalHost) && ignoreLocalHostName)
+            return;
+
+        String hostName = addr.getHostName();
+
         if (allHostNames)
             hostNames.add(hostName);
         else if (!F.isEmpty(hostName) && !addr.isLoopbackAddress())
@@ -9650,23 +9665,65 @@
             String hostName = hostNamesIt.hasNext() ? hostNamesIt.next() : null;
 
             if (!F.isEmpty(hostName)) {
-                InetSocketAddress inetSockAddr = new InetSocketAddress(hostName, port);
+                InetSocketAddress inetSockAddr = createResolved(hostName, port);
 
-                if (inetSockAddr.isUnresolved() || inetSockAddr.getAddress().isLoopbackAddress())
-                    inetSockAddr = new InetSocketAddress(addr, port);
+                if (inetSockAddr.isUnresolved() ||
+                    (!inetSockAddr.isUnresolved() && inetSockAddr.getAddress().isLoopbackAddress())
+                )
+                    inetSockAddr = createResolved(addr, port);
 
                 res.add(inetSockAddr);
             }
 
             // Always append address because local and remote nodes may have the same hostname
-            // therefore remote hostname will be always resolved to local address.
-            res.add(new InetSocketAddress(addr, port));
+            // therefore remote hostname will always be resolved to local address.
+            res.add(createResolved(addr, port));
         }
 
         return res;
     }
 
     /**
+     * Creates a resolved inet socket address, writing the diagnostic information into a log if operation took
+     * a significant amount of time.
+     *
+     * @param addr Host address.
+     * @param port Port value.
+     * @return Resolved address.
+     */
+    private static InetSocketAddress createResolved(String addr, int port) {
+        log.log(Level.FINE, () -> S.toString(
+            "Resolving address",
+            "addr", addr, false,
+            "port", port, false,
+            "thread", Thread.currentThread().getName(), false
+        ));
+
+        long startNanos = System.nanoTime();
+
+        try {
+            return new InetSocketAddress(addr, port);
+        }
+        finally {
+            long endNanos = System.nanoTime();
+
+            long duration = endNanos - startNanos;
+
+            long threshold = U.millisToNanos(200);
+
+            if (duration > threshold) {
+                log.log(Level.FINE, new TimeoutException(), () -> S.toString(
+                    "Resolving address took too much time",
+                    "duration(ms)", U.nanosToMillis(duration), false,
+                    "addr", addr, false,
+                    "port", port, false,
+                    "thread", Thread.currentThread().getName(), false
+                ));
+            }
+        }
+    }
+
+    /**
      * Resolves all not loopback addresses and collect results.
      *
      * @param addrRslvr Address resolver.
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/util/IgniteUtilsSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/util/IgniteUtilsSelfTest.java
index 1800077..9fbd2b9 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/util/IgniteUtilsSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/util/IgniteUtilsSelfTest.java
@@ -62,6 +62,7 @@
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteInterruptedException;
+import org.apache.ignite.IgniteSystemProperties;
 import org.apache.ignite.cluster.ClusterGroup;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.compute.ComputeJob;
@@ -79,6 +80,7 @@
 import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNode;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.http.GridEmbeddedHttpServer;
+import org.apache.ignite.testframework.junits.WithSystemProperty;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.apache.ignite.testframework.junits.common.GridCommonTest;
 import org.apache.ignite.thread.IgniteThreadFactory;
@@ -1484,6 +1486,64 @@
     }
 
     /**
+     * Tests that local hostname is ignored if {@link IgniteSystemProperties#IGNITE_IGNORE_LOCAL_HOST_NAME} is
+     * set to {@code true}.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    @WithSystemProperty(key = IgniteSystemProperties.IGNITE_LOCAL_HOST, value = "example.com")
+    @WithSystemProperty(key = IgniteSystemProperties.IGNITE_IGNORE_LOCAL_HOST_NAME, value = "true")
+    public void testResolveLocalAddressesWithHostNameDefined() throws Exception {
+        testAddressResolveWithLocalHostDefined();
+    }
+
+    /**
+     * Tests that local hostname is not ignored if {@link IgniteSystemProperties#IGNITE_IGNORE_LOCAL_HOST_NAME} is
+     * set to {@code false}.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    @WithSystemProperty(key = IgniteSystemProperties.IGNITE_LOCAL_HOST, value = "example.com")
+    @WithSystemProperty(key = IgniteSystemProperties.IGNITE_IGNORE_LOCAL_HOST_NAME, value = "false")
+    public void testResolveLocalAddressesWithHostNameDefinedAndLocalHostNameNotIgnored() throws Exception {
+        testAddressResolveWithLocalHostDefined();
+    }
+
+    /**
+     * Tests {@link IgniteUtils#resolveLocalAddresses(InetAddress)} with different values set to
+     * {@link IgniteSystemProperties#IGNITE_LOCAL_HOST} and {@link IgniteSystemProperties#IGNITE_IGNORE_LOCAL_HOST_NAME}.
+     *
+     * @throws Exception If failed.
+     */
+    private void testAddressResolveWithLocalHostDefined() throws Exception {
+        try {
+            boolean ignoreLocalHostname = IgniteSystemProperties.getBoolean(IgniteSystemProperties.IGNITE_IGNORE_LOCAL_HOST_NAME);
+            String userDefinedHost = IgniteSystemProperties.getString(IgniteSystemProperties.IGNITE_LOCAL_HOST);
+
+            InetSocketAddress inetSocketAddress = new InetSocketAddress(userDefinedHost, 0);
+            InetAddress addr = inetSocketAddress.getAddress();
+            IgniteBiTuple<Collection<String>, Collection<String>> localAddresses = IgniteUtils.resolveLocalAddresses(addr);
+
+            if (ignoreLocalHostname) {
+                // If local hostname is ignored, then no hostname should be resolved.
+                assertTrue(localAddresses.get2().isEmpty());
+            }
+            else {
+                // If local hostname is not ignored, then we should receive example.com.
+                assertFalse(localAddresses.get2().isEmpty());
+                assertEquals("example.com", F.first(localAddresses.get2()));
+            }
+        }
+        finally {
+            // Clear local address cache as we have polluted it with this test.
+            GridTestUtils.setFieldValue(IgniteUtils.class, "cachedLocalAddrAllHostNames", null);
+            GridTestUtils.setFieldValue(IgniteUtils.class, "cachedLocalAddr", null);
+        }
+    }
+
+    /**
      * Test enum.
      */
     private enum TestEnum {