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

petrov-mg pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new 0b35e02c618 IGNITE-28751 Refactored TCP Discovery SPI joining node 
validation (#13217)
0b35e02c618 is described below

commit 0b35e02c6185622f627efee2f0f5272e5e02360a
Author: Mikhail Petrov <[email protected]>
AuthorDate: Thu Jun 11 01:21:40 2026 +0300

    IGNITE-28751 Refactored TCP Discovery SPI joining node validation (#13217)
---
 .../org/apache/ignite/events/DiscoveryEvent.java   |   1 +
 .../java/org/apache/ignite/events/EventType.java   |   2 +
 .../ignite/events/NodeValidationFailedEvent.java   |  13 +-
 .../managers/discovery/GridDiscoveryManager.java   |  10 +
 .../rollingupgrade/RollingUpgradeProcessor.java    |  63 ++-
 .../ignite/spi/discovery/tcp/ClientImpl.java       |  13 +-
 .../ignite/spi/discovery/tcp/ServerImpl.java       | 577 +++++++++------------
 .../ignite/spi/discovery/tcp/TcpDiscoverySpi.java  |  11 -
 .../tcp/internal/TcpDiscoveryNodesRing.java        |  47 --
 .../ignite/internal/GridReleaseTypeSelfTest.java   |   4 -
 .../IgniteNodeValidationFailedEventTest.java       |   5 +-
 11 files changed, 297 insertions(+), 449 deletions(-)

diff --git 
a/modules/core/src/main/java/org/apache/ignite/events/DiscoveryEvent.java 
b/modules/core/src/main/java/org/apache/ignite/events/DiscoveryEvent.java
index 0f4b004e4f5..f05873f0a9c 100644
--- a/modules/core/src/main/java/org/apache/ignite/events/DiscoveryEvent.java
+++ b/modules/core/src/main/java/org/apache/ignite/events/DiscoveryEvent.java
@@ -62,6 +62,7 @@ import org.jetbrains.annotations.Nullable;
  * @see EventType#EVT_NODE_JOINED
  * @see EventType#EVT_NODE_LEFT
  * @see EventType#EVT_NODE_SEGMENTED
+ * @see EventType#EVT_NODE_VALIDATION_FAILED
  * @see EventType#EVTS_DISCOVERY_ALL
  * @see EventType#EVTS_DISCOVERY
  */
diff --git a/modules/core/src/main/java/org/apache/ignite/events/EventType.java 
b/modules/core/src/main/java/org/apache/ignite/events/EventType.java
index 669faab998b..0e4ed8ba252 100644
--- a/modules/core/src/main/java/org/apache/ignite/events/EventType.java
+++ b/modules/core/src/main/java/org/apache/ignite/events/EventType.java
@@ -1102,6 +1102,7 @@ public interface EventType {
         EVT_NODE_JOINED,
         EVT_NODE_LEFT,
         EVT_NODE_FAILED,
+        EVT_NODE_VALIDATION_FAILED,
         EVT_NODE_SEGMENTED,
         EVT_CLIENT_NODE_DISCONNECTED,
         EVT_CLIENT_NODE_RECONNECTED
@@ -1119,6 +1120,7 @@ public interface EventType {
         EVT_NODE_LEFT,
         EVT_NODE_FAILED,
         EVT_NODE_SEGMENTED,
+        EVT_NODE_VALIDATION_FAILED,
         EVT_NODE_METRICS_UPDATED,
         EVT_CLIENT_NODE_DISCONNECTED,
         EVT_CLIENT_NODE_RECONNECTED
diff --git 
a/modules/core/src/main/java/org/apache/ignite/events/NodeValidationFailedEvent.java
 
b/modules/core/src/main/java/org/apache/ignite/events/NodeValidationFailedEvent.java
index 74cf3a9f5a1..312cfb4179e 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/events/NodeValidationFailedEvent.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/events/NodeValidationFailedEvent.java
@@ -31,13 +31,10 @@ import static 
org.apache.ignite.events.EventType.EVT_NODE_VALIDATION_FAILED;
  * @see EventType#EVT_NODE_VALIDATION_FAILED
  * @see GridComponent#validateNode
  */
-public class NodeValidationFailedEvent extends EventAdapter {
+public class NodeValidationFailedEvent extends DiscoveryEvent {
     /** */
     private static final long serialVersionUID = 0L;
 
-    /** The node that attempted to join cluster. */
-    private final ClusterNode evtNode;
-
     /** Validation result. */
     private final IgniteNodeValidationResult res;
 
@@ -49,17 +46,11 @@ public class NodeValidationFailedEvent extends EventAdapter 
{
      * @param res Joining node validation result.
      */
     public NodeValidationFailedEvent(ClusterNode node, ClusterNode evtNode, 
IgniteNodeValidationResult res) {
-        super(node, res.message(), EVT_NODE_VALIDATION_FAILED);
+        super(node, res.message(), EVT_NODE_VALIDATION_FAILED, evtNode);
 
-        this.evtNode = evtNode;
         this.res = res;
     }
 
-    /** @return Node that couldn't join the topology due to a validation 
failure. */
-    public ClusterNode eventNode() {
-        return evtNode;
-    }
-
     /** @return Joining node validation result. */
     public IgniteNodeValidationResult validationResult() {
         return res;
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/GridDiscoveryManager.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/GridDiscoveryManager.java
index 4bfaa20f304..da9b54b2440 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/GridDiscoveryManager.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/GridDiscoveryManager.java
@@ -1895,6 +1895,16 @@ public class GridDiscoveryManager extends 
GridManagerAdapter<DiscoverySpi> {
         return fut;
     }
 
+    /**
+     * Returns a collection of all remote nodes known to the underlying {@link 
DiscoverySpi} implementation.
+     *
+     * <p>Unlike {@link #remoteNodes()}, this method may include nodes that 
have successfully completed
+     * validation but have not yet completed their join routine.</p>
+     */
+    public Collection<ClusterNode> discoverySpiRemoteNodes() {
+        return getSpi().getRemoteNodes();
+    }
+
     /**
      * Gets discovery collection cache from SPI safely guarding against 
"floating" collections.
      *
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java
index b14e6a80423..f9ddc8cc871 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/RollingUpgradeProcessor.java
@@ -18,15 +18,15 @@
 package org.apache.ignite.internal.processors.rollingupgrade;
 
 import java.util.Objects;
+import java.util.SortedSet;
+import java.util.TreeSet;
 import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.events.DiscoveryEvent;
-import org.apache.ignite.events.Event;
 import org.apache.ignite.internal.GridKernalContext;
-import org.apache.ignite.internal.managers.eventstorage.GridLocalEventListener;
 import org.apache.ignite.internal.processors.GridProcessorAdapter;
 import 
org.apache.ignite.internal.processors.metastorage.DistributedMetaStorage;
 import 
org.apache.ignite.internal.processors.metastorage.DistributedMetastorageLifecycleListener;
@@ -39,14 +39,13 @@ import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteProductVersion;
 import org.apache.ignite.plugin.security.SecurityPermission;
 import org.apache.ignite.spi.IgniteNodeValidationResult;
-import org.apache.ignite.spi.discovery.DiscoverySpi;
 import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
-import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNodesRing;
 import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.events.EventType.EVT_NODE_FAILED;
 import static org.apache.ignite.events.EventType.EVT_NODE_JOINED;
 import static org.apache.ignite.events.EventType.EVT_NODE_LEFT;
+import static org.apache.ignite.events.EventType.EVT_NODE_VALIDATION_FAILED;
 import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_BUILD_VER;
 import static 
org.apache.ignite.internal.processors.metastorage.DistributedMetaStorage.IGNITE_INTERNAL_KEY_PREFIX;
 
@@ -58,15 +57,9 @@ public class RollingUpgradeProcessor extends 
GridProcessorAdapter implements Dis
     /** Metastorage with the write access. */
     @Nullable private volatile DistributedMetaStorage metastorage;
 
-    /** TCP discovery nodes ring. */
-    private TcpDiscoveryNodesRing ring;
-
     /** Last joining node. */
     private ClusterNode lastJoiningNode;
 
-    /** Last joining node timestamp. */
-    private long lastJoiningNodeTimestamp;
-
     /** Lock for synchronization between tcp-disco-msg-worker thread and 
management operations. */
     private final Object lock = new Object();
 
@@ -85,26 +78,25 @@ public class RollingUpgradeProcessor extends 
GridProcessorAdapter implements Dis
 
     /** {@inheritDoc} */
     @Override public void onKernalStart(boolean active) throws 
IgniteCheckedException {
-        DiscoverySpi spi = ctx.config().getDiscoverySpi();
-
-        if (spi instanceof TcpDiscoverySpi)
-            ring = ((TcpDiscoverySpi)spi).discoveryRing();
-
         startLatch.countDown();
     }
 
     /** {@inheritDoc} */
     @Override public void start() throws IgniteCheckedException {
-        ctx.event().addLocalEventListener(new GridLocalEventListener() {
-            @Override public void onEvent(Event evt) {
+        ctx.event().addLocalEventListener(
+            evt -> {
                 UUID nodeId = ((DiscoveryEvent)evt).eventNode().id();
 
                 synchronized (lock) {
                     if (lastJoiningNode != null && 
lastJoiningNode.id().equals(nodeId))
                         lastJoiningNode = null;
                 }
-            }
-        }, EVT_NODE_JOINED, EVT_NODE_FAILED, EVT_NODE_LEFT);
+            },
+            EVT_NODE_JOINED,
+            EVT_NODE_FAILED,
+            EVT_NODE_LEFT,
+            EVT_NODE_VALIDATION_FAILED
+        );
 
         
ctx.internalSubscriptionProcessor().registerDistributedMetastorageListener(new 
DistributedMetastorageLifecycleListener() {
             @Override public void onReadyForWrite(DistributedMetaStorage 
metastorage) {
@@ -131,8 +123,6 @@ public class RollingUpgradeProcessor extends 
GridProcessorAdapter implements Dis
     @Override public @Nullable IgniteNodeValidationResult 
validateNode(ClusterNode node) {
         synchronized (lock) {
             lastJoiningNode = node;
-
-            lastJoiningNodeTimestamp = U.currentTimeMillis();
         }
 
         ClusterNode locNode = ctx.discovery().localNode();
@@ -247,21 +237,14 @@ public class RollingUpgradeProcessor extends 
GridProcessorAdapter implements Dis
         if (rollUpVers == null)
             return;
 
-        IgnitePair<IgniteProductVersion> minMaxVerPair = 
ring.minMaxNodeVersions();
-
-        if (!minMaxVerPair.get1().equals(minMaxVerPair.get2()))
-            throw new IgniteCheckedException("Can't disable rolling upgrade 
with different versions in cluster: "
-                + minMaxVerPair.get1() + ", " + minMaxVerPair.get2());
+        IgnitePair<IgniteProductVersion> minMaxVerPair;
 
         synchronized (lock) {
-            if (lastJoiningNode != null) {
-                // Use 3 * joinTimeout as an upper time bound for joining 
nodes that may drop during validation
-                // without sending NODE_LEFT / NODE_FAILED events.
-                long timeout = 
((TcpDiscoverySpi)ctx.config().getDiscoverySpi()).getJoinTimeout() * 3;
+            minMaxVerPair = resolveMinMaxNodeVersions();
 
-                if (ring.node(lastJoiningNode.id()) != null || (timeout > 0 && 
U.currentTimeMillis() - lastJoiningNodeTimestamp > timeout))
-                    lastJoiningNode = null;
-            }
+            if (!minMaxVerPair.get1().equals(minMaxVerPair.get2()))
+                throw new IgniteCheckedException("Can't disable rolling 
upgrade with different versions in cluster: "
+                    + minMaxVerPair.get1() + ", " + minMaxVerPair.get2());
 
             if (lastJoiningNode != null) {
                 IgniteProductVersion lastJoiningNodeVer = 
IgniteProductVersion.fromString(lastJoiningNode.attribute(ATTR_BUILD_VER));
@@ -280,6 +263,20 @@ public class RollingUpgradeProcessor extends 
GridProcessorAdapter implements Dis
             log.info("Rolling upgrade disabled. Current version of nodes in 
cluster: " + minMaxVerPair.get1());
     }
 
+    /** */
+    private IgnitePair<IgniteProductVersion> resolveMinMaxNodeVersions() {
+        assert Thread.holdsLock(lock);
+
+        SortedSet<IgniteProductVersion> clusterNodes = new TreeSet<>();
+
+        for (ClusterNode node : ctx.discovery().discoverySpiRemoteNodes())
+            clusterNodes.add(node.version());
+
+        clusterNodes.add(ctx.discovery().localNode().version());
+
+        return new IgnitePair<>(clusterNodes.first(), clusterNodes.last());
+    }
+
     /**
      * Returns a pair containing the current and target versions of the 
cluster.
      * <p>
diff --git 
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ClientImpl.java
 
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ClientImpl.java
index d3a5f13fe23..a0e1a200487 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ClientImpl.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ClientImpl.java
@@ -251,7 +251,7 @@ class ClientImpl extends TcpDiscoveryImpl {
 
     /** {@inheritDoc} */
     @Override public void dumpRingStructure(IgniteLogger log) {
-        ClusterNode[] serverNodes = getRemoteNodes().stream()
+        ClusterNode[] serverNodes = remoteVisibleNodes().stream()
                 .filter(node -> !node.isClient())
                 .sorted(Comparator.comparingLong(ClusterNode::order))
                 .toArray(ClusterNode[]::new);
@@ -371,7 +371,7 @@ class ClientImpl extends TcpDiscoveryImpl {
 
     /** {@inheritDoc} */
     @Override public Collection<ClusterNode> getRemoteNodes() {
-        return U.arrayList(rmtNodes.values(), 
TcpDiscoveryNodesRing.VISIBLE_NODES);
+        return U.arrayList(rmtNodes.values());
     }
 
     /** {@inheritDoc} */
@@ -465,7 +465,7 @@ class ClientImpl extends TcpDiscoveryImpl {
 
         spi.getSpiContext().deregisterPorts();
 
-        Collection<ClusterNode> rmts = getRemoteNodes();
+        Collection<ClusterNode> rmts = remoteVisibleNodes();
 
         // This is restart/disconnection and remote nodes are not empty.
         // We need to fire FAIL event for each.
@@ -1077,6 +1077,11 @@ class ClientImpl extends TcpDiscoveryImpl {
         return ignite instanceof IgniteEx ? 
((IgniteEx)ignite).context().workersRegistry() : null;
     }
 
+    /** */
+    private Collection<ClusterNode> remoteVisibleNodes() {
+        return U.arrayList(rmtNodes.values(), 
TcpDiscoveryNodesRing.VISIBLE_NODES);
+    }
+
     /**
      * Metrics sender.
      */
@@ -2277,7 +2282,7 @@ class ClientImpl extends TcpDiscoveryImpl {
                 return;
 
             if (log.isInfoEnabled()) {
-                for (ClusterNode node : getRemoteNodes()) {
+                for (ClusterNode node : remoteVisibleNodes()) {
                     if (node.id().equals(locNode.clientRouterNodeId())) {
                         if (log.isInfoEnabled())
                             log.info("Router node: " + node);
diff --git 
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java
 
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java
index 0b25f0a2386..82c012c2a1a 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java
@@ -59,6 +59,7 @@ import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import javax.net.ssl.SSLException;
@@ -167,10 +168,10 @@ import org.jetbrains.annotations.Nullable;
 
 import static java.util.stream.Collectors.collectingAndThen;
 import static java.util.stream.Collectors.toList;
-import static 
org.apache.ignite.IgniteSystemProperties.IGNITE_BINARY_MARSHALLER_USE_STRING_SERIALIZATION_VER_2;
+import static 
org.apache.ignite.IgniteCommonsSystemProperties.IGNITE_BINARY_MARSHALLER_USE_STRING_SERIALIZATION_VER_2;
+import static 
org.apache.ignite.IgniteCommonsSystemProperties.IGNITE_OPTIMIZED_MARSHALLER_USE_DEFAULT_SUID;
 import static 
org.apache.ignite.IgniteSystemProperties.IGNITE_DISCOVERY_CLIENT_RECONNECT_HISTORY_SIZE;
 import static 
org.apache.ignite.IgniteSystemProperties.IGNITE_NODE_IDS_HISTORY_SIZE;
-import static 
org.apache.ignite.IgniteSystemProperties.IGNITE_OPTIMIZED_MARSHALLER_USE_DEFAULT_SUID;
 import static org.apache.ignite.IgniteSystemProperties.getInteger;
 import static org.apache.ignite.events.EventType.EVT_NODE_FAILED;
 import static org.apache.ignite.events.EventType.EVT_NODE_JOINED;
@@ -424,7 +425,7 @@ class ServerImpl extends TcpDiscoveryImpl {
 
     /** {@inheritDoc} */
     @Override public Collection<ClusterNode> getRemoteNodes() {
-        return upcast(ring.visibleRemoteNodes());
+        return upcast(ring.remoteNodes());
     }
 
     /** {@inheritDoc} */
@@ -2907,6 +2908,18 @@ class ServerImpl extends TcpDiscoveryImpl {
      * Message worker for discovery messages processing.
      */
     protected class RingMessageWorker extends 
MessageWorker<TcpDiscoveryAbstractMessage> {
+        /** */
+        private final Collection<Function<TcpDiscoveryJoinRequestMessage, 
IgniteNodeValidationResult>> nodeValidators = Arrays.asList(
+            this::validateByIgniteComponents,
+            this::validateByIgniteComponentsWithJoiningNodeData,
+            this::validateMarshallerName,
+            this::validateMarshallerSuid,
+            this::validateMarshallerCompactFooter,
+            this::validateStringSerializationVersion,
+            this::validateLateAffinityAssignment,
+            this::validateDataCenterId
+        );
+
         /** Next node. */
         private TcpDiscoveryNode next;
 
@@ -4523,60 +4536,10 @@ class ServerImpl extends TcpDiscoveryImpl {
                 }
 
                 if (err == null)
-                    err = spi.getSpiContext().validateNode(node);
-
-                if (err == null) {
-                    DiscoveryDataBag data = 
msg.gridDiscoveryData().bagWithJoiningNodeData();
-
-                    err = spi.getSpiContext().validateNode(node, data);
-                }
+                    err = validateJoiningNode(msg);
 
                 if (err != null) {
-                    final IgniteNodeValidationResult err0 = err;
-
-                    if (log.isDebugEnabled())
-                        log.debug("Node validation failed [res=" + err + ", 
node=" + node + ']');
-
-                    utilityPool.execute(
-                        new Runnable() {
-                            @Override public void run() {
-                                spi.getSpiContext().recordEvent(new 
NodeValidationFailedEvent(locNode, node, err0));
-
-                                boolean ping = node.id().equals(err0.nodeId()) 
? pingNode(node) : pingNode(err0.nodeId());
-
-                                if (!ping) {
-                                    if (log.isDebugEnabled()) {
-                                        log.debug("Conflicting node has 
already left, need to wait for event. " +
-                                            "Will ignore join request for now 
since it will be recent [req=" + msg +
-                                            ", err=" + err0.message() + ']');
-                                    }
-
-                                    // Ignore join request.
-                                    return;
-                                }
-
-                                LT.warn(log, err0.message());
-
-                                // Always output in debug.
-                                if (log.isDebugEnabled())
-                                    log.debug(err0.message());
-
-                                try {
-                                    trySendMessageDirectly(node,
-                                        new 
TcpDiscoveryCheckFailedMessage(err0.nodeId(), err0.sendMessage()));
-                                }
-                                catch (IgniteSpiException e) {
-                                    if (log.isDebugEnabled()) {
-                                        log.debug("Failed to send hash ID 
resolver validation failed message to node " +
-                                            "[node=" + node + ", err=" + 
e.getMessage() + ']');
-                                    }
-
-                                    onException("Failed to send hash ID 
resolver validation failed message to node " +
-                                        "[node=" + node + ", err=" + 
e.getMessage() + ']', e);
-                                }
-                            }
-                        }
-                    );
+                    sendJoiningNodeCheckFailedResponse(msg, err);
 
                     // Ignore join request.
                     msg.spanContainer().span()
@@ -4587,336 +4550,278 @@ class ServerImpl extends TcpDiscoveryImpl {
                     return;
                 }
 
-                final String locMarsh = locNode.attribute(ATTR_MARSHALLER);
-                final String rmtMarsh = node.attribute(ATTR_MARSHALLER);
+                // Handle join.
+                node.internalOrder(ring.nextNodeOrder());
 
-                if (!Objects.equals(locMarsh, rmtMarsh)) {
-                    utilityPool.execute(
-                        new Runnable() {
-                            @Override public void run() {
-                                String errMsg = "Local node's marshaller 
differs from remote node's marshaller " +
-                                    "(to make sure all nodes in topology have 
identical marshaller, " +
-                                    "configure marshaller explicitly in 
configuration) " +
-                                    "[locMarshaller=" + locMarsh + ", 
rmtMarshaller=" + rmtMarsh +
-                                    ", locNodeAddrs=" + 
U.addressesAsString(locNode) +
-                                    ", rmtNodeAddrs=" + 
U.addressesAsString(node) +
-                                    ", locNodeId=" + locNode.id() + ", 
rmtNodeId=" + msg.creatorNodeId() + ']';
+                if (log.isDebugEnabled())
+                    log.debug("Internal order has been assigned to node: " + 
node);
 
-                                LT.warn(log, errMsg);
+                DiscoveryDataPacket data = msg.gridDiscoveryData();
 
-                                // Always output in debug.
-                                if (log.isDebugEnabled())
-                                    log.debug(errMsg);
+                TcpDiscoveryNodeAddedMessage nodeAddedMsg = new 
TcpDiscoveryNodeAddedMessage(locNodeId,
+                    node, data, spi.gridStartTime);
 
-                                try {
-                                    String sndMsg = "Local node's marshaller 
differs from remote node's marshaller " +
-                                        "(to make sure all nodes in topology 
have identical marshaller, " +
-                                        "configure marshaller explicitly in 
configuration) " +
-                                        "[locMarshaller=" + rmtMarsh + ", 
rmtMarshaller=" + locMarsh +
-                                        ", locNodeAddrs=" + 
U.addressesAsString(node) + ", locPort=" + node.discoveryPort() +
-                                        ", rmtNodeAddr=" + 
U.addressesAsString(locNode) + ", locNodeId=" + node.id() +
-                                        ", rmtNodeId=" + locNode.id() + ']';
-
-                                    trySendMessageDirectly(node,
-                                        new 
TcpDiscoveryCheckFailedMessage(locNodeId, sndMsg));
-                                }
-                                catch (IgniteSpiException e) {
-                                    if (log.isDebugEnabled())
-                                        log.debug("Failed to send marshaller 
check failed message to node " +
-                                            "[node=" + node + ", err=" + 
e.getMessage() + ']');
+                nodeAddedMsg = tracing.messages().branch(nodeAddedMsg, msg);
 
-                                    onException("Failed to send marshaller 
check failed message to node " +
-                                        "[node=" + node + ", err=" + 
e.getMessage() + ']', e);
-                                }
-                            }
-                        }
-                    );
+                nodeAddedMsg.client(msg.client());
 
-                    // Ignore join request.
-                    msg.spanContainer().span()
-                        .addLog(() -> "Ignored")
-                        .setStatus(SpanStatus.ABORTED)
-                        .end();
+                processNodeAddedMessage(nodeAddedMsg);
 
-                    return;
-                }
+                tracing.messages().finishProcessing(nodeAddedMsg);
+            }
+            else {
+                if (sendMessageToRemotes(msg))
+                    sendMessageAcrossRing(msg);
+            }
+        }
 
-                // If node have no value for this attribute then we treat it 
as true.
-                final Boolean locMarshUseDfltSuid = 
locNode.attribute(ATTR_MARSHALLER_USE_DFLT_SUID);
-                boolean locMarshUseDfltSuidBool = locMarshUseDfltSuid == null 
? true : locMarshUseDfltSuid;
+        /**
+         * @param node Node.
+         * @param name Attribute name.
+         * @param dflt Default value.
+         * @return Attribute value.
+         */
+        private boolean booleanAttribute(ClusterNode node, String name, 
boolean dflt) {
+            Boolean attr = node.attribute(name);
 
-                final Boolean rmtMarshUseDfltSuid = 
node.attribute(ATTR_MARSHALLER_USE_DFLT_SUID);
-                boolean rmtMarshUseDfltSuidBool = rmtMarshUseDfltSuid == null 
? true : rmtMarshUseDfltSuid;
+            return attr != null ? attr : dflt;
+        }
 
-                Boolean locLateAssign = 
locNode.attribute(ATTR_LATE_AFFINITY_ASSIGNMENT);
-                // Can be null only in tests.
-                boolean locLateAssignBool = locLateAssign != null ? 
locLateAssign : false;
+        /** */
+        private IgniteNodeValidationResult 
validateMarshallerName(TcpDiscoveryJoinRequestMessage req) {
+            String locMarsh = locNode.attribute(ATTR_MARSHALLER);
+            String rmtMarsh = req.node().attribute(ATTR_MARSHALLER);
 
-                if (locMarshUseDfltSuidBool != rmtMarshUseDfltSuidBool) {
-                    utilityPool.execute(
-                        new Runnable() {
-                            @Override public void run() {
-                                String errMsg = "Local node's " + 
IGNITE_OPTIMIZED_MARSHALLER_USE_DEFAULT_SUID +
-                                    " property value differs from remote 
node's value " +
-                                    "(to make sure all nodes in topology have 
identical marshaller settings, " +
-                                    "configure system property explicitly) " +
-                                    "[locMarshUseDfltSuid=" + 
locMarshUseDfltSuid +
-                                    ", rmtMarshUseDfltSuid=" + 
rmtMarshUseDfltSuid +
-                                    ", locNodeAddrs=" + 
U.addressesAsString(locNode) +
-                                    ", rmtNodeAddrs=" + 
U.addressesAsString(node) +
-                                    ", locNodeId=" + locNode.id() + ", 
rmtNodeId=" + msg.creatorNodeId() + ']';
-
-                                String sndMsg = "Local node's " + 
IGNITE_OPTIMIZED_MARSHALLER_USE_DEFAULT_SUID +
-                                    " property value differs from remote 
node's value " +
-                                    "(to make sure all nodes in topology have 
identical marshaller settings, " +
-                                    "configure system property explicitly) " +
-                                    "[locMarshUseDfltSuid=" + 
rmtMarshUseDfltSuid +
-                                    ", rmtMarshUseDfltSuid=" + 
locMarshUseDfltSuid +
-                                    ", locNodeAddrs=" + 
U.addressesAsString(node) + ", locPort=" + node.discoveryPort() +
-                                    ", rmtNodeAddr=" + 
U.addressesAsString(locNode) + ", locNodeId=" + node.id() +
-                                    ", rmtNodeId=" + locNode.id() + ']';
-
-                                nodeCheckError(
-                                    node,
-                                    errMsg,
-                                    sndMsg);
-                            }
-                        });
+            if (Objects.equals(locMarsh, rmtMarsh))
+                return null;
 
-                    // Ignore join request.
-                    msg.spanContainer().span()
-                        .addLog(() -> "Ignored")
-                        .setStatus(SpanStatus.ABORTED)
-                        .end();
+            String errMsg = "Local node's marshaller differs from remote 
node's marshaller " +
+                "(to make sure all nodes in topology have identical 
marshaller, " +
+                "configure marshaller explicitly in configuration) " +
+                "[locMarshaller=" + locMarsh + ", rmtMarshaller=" + rmtMarsh +
+                ", locNodeAddrs=" + U.addressesAsString(locNode) +
+                ", rmtNodeAddrs=" + U.addressesAsString(req.node()) +
+                ", locNodeId=" + locNode.id() + ", rmtNodeId=" + 
req.creatorNodeId() + ']';
 
-                    return;
-                }
+            String sndMsg = "Local node's marshaller differs from remote 
node's marshaller " +
+                "(to make sure all nodes in topology have identical 
marshaller, " +
+                "configure marshaller explicitly in configuration) " +
+                "[locMarshaller=" + rmtMarsh + ", rmtMarshaller=" + locMarsh +
+                ", locNodeAddrs=" + U.addressesAsString(req.node()) + ", 
locPort=" + req.node().discoveryPort() +
+                ", rmtNodeAddr=" + U.addressesAsString(locNode) + ", 
locNodeId=" + req.node().id() +
+                ", rmtNodeId=" + locNode.id() + ']';
 
-                // Validate compact footer flags.
-                Boolean locMarshCompactFooter = 
locNode.attribute(ATTR_MARSHALLER_COMPACT_FOOTER);
-                final boolean locMarshCompactFooterBool = 
locMarshCompactFooter != null ? locMarshCompactFooter : false;
+            return new IgniteNodeValidationResult(req.node().id(), errMsg, 
sndMsg);
+        }
 
-                Boolean rmtMarshCompactFooter = 
node.attribute(ATTR_MARSHALLER_COMPACT_FOOTER);
-                final boolean rmtMarshCompactFooterBool = 
rmtMarshCompactFooter != null ? rmtMarshCompactFooter : false;
+        /** */
+        private IgniteNodeValidationResult 
validateMarshallerSuid(TcpDiscoveryJoinRequestMessage req) {
+            boolean locMarshUseDfltSuid = booleanAttribute(locNode, 
ATTR_MARSHALLER_USE_DFLT_SUID, true);
+            boolean rmtMarshUseDfltSuid = booleanAttribute(req.node(), 
ATTR_MARSHALLER_USE_DFLT_SUID, true);
 
-                if (locMarshCompactFooterBool != rmtMarshCompactFooterBool) {
-                    utilityPool.execute(
-                        new Runnable() {
-                            @Override public void run() {
-                                String errMsg = "Local node's binary 
marshaller \"compactFooter\" property differs from " +
-                                    "the same property on remote node (make 
sure all nodes in topology have the same value " +
-                                    "of \"compactFooter\" property) 
[locMarshallerCompactFooter=" + locMarshCompactFooterBool +
-                                    ", rmtMarshallerCompactFooter=" + 
rmtMarshCompactFooterBool +
-                                    ", locNodeAddrs=" + 
U.addressesAsString(locNode) +
-                                    ", rmtNodeAddrs=" + 
U.addressesAsString(node) +
-                                    ", locNodeId=" + locNode.id() + ", 
rmtNodeId=" + msg.creatorNodeId() + ']';
-
-                                String sndMsg = "Local node's binary 
marshaller \"compactFooter\" property differs from " +
-                                    "the same property on remote node (make 
sure all nodes in topology have the same value " +
-                                    "of \"compactFooter\" property) 
[locMarshallerCompactFooter=" + rmtMarshCompactFooterBool +
-                                    ", rmtMarshallerCompactFooter=" + 
locMarshCompactFooterBool +
-                                    ", locNodeAddrs=" + 
U.addressesAsString(node) + ", locPort=" + node.discoveryPort() +
-                                    ", rmtNodeAddr=" + 
U.addressesAsString(locNode) + ", locNodeId=" + node.id() +
-                                    ", rmtNodeId=" + locNode.id() + ']';
-
-                                nodeCheckError(
-                                    node,
-                                    errMsg,
-                                    sndMsg);
-                            }
-                        });
+            if (locMarshUseDfltSuid == rmtMarshUseDfltSuid)
+                return null;
 
-                    // Ignore join request.
-                    msg.spanContainer().span()
-                        .addLog(() -> "Ignored")
-                        .setStatus(SpanStatus.ABORTED)
-                        .end();
+            String errMsg = "Local node's " + 
IGNITE_OPTIMIZED_MARSHALLER_USE_DEFAULT_SUID +
+                " property value differs from remote node's value " +
+                "(to make sure all nodes in topology have identical marshaller 
settings, " +
+                "configure system property explicitly) " +
+                "[locMarshUseDfltSuid=" + locMarshUseDfltSuid +
+                ", rmtMarshUseDfltSuid=" + rmtMarshUseDfltSuid +
+                ", locNodeAddrs=" + U.addressesAsString(locNode) +
+                ", rmtNodeAddrs=" + U.addressesAsString(req.node()) +
+                ", locNodeId=" + locNode.id() + ", rmtNodeId=" + 
req.creatorNodeId() + ']';
+
+            String sndMsg = "Local node's " + 
IGNITE_OPTIMIZED_MARSHALLER_USE_DEFAULT_SUID +
+                " property value differs from remote node's value " +
+                "(to make sure all nodes in topology have identical marshaller 
settings, " +
+                "configure system property explicitly) " +
+                "[locMarshUseDfltSuid=" + rmtMarshUseDfltSuid +
+                ", rmtMarshUseDfltSuid=" + locMarshUseDfltSuid +
+                ", locNodeAddrs=" + U.addressesAsString(req.node()) + ", 
locPort=" + req.node().discoveryPort() +
+                ", rmtNodeAddr=" + U.addressesAsString(locNode) + ", 
locNodeId=" + req.node().id() +
+                ", rmtNodeId=" + locNode.id() + ']';
+
+            return new IgniteNodeValidationResult(req.node().id(), errMsg, 
sndMsg);
+        }
 
-                    return;
-                }
+        /** */
+        private IgniteNodeValidationResult 
validateMarshallerCompactFooter(TcpDiscoveryJoinRequestMessage req) {
+            boolean locMarshCompactFooter = booleanAttribute(locNode, 
ATTR_MARSHALLER_COMPACT_FOOTER, false);
+            boolean rmtMarshCompactFooter = booleanAttribute(req.node(), 
ATTR_MARSHALLER_COMPACT_FOOTER, false);
 
-                // Validate String serialization mechanism used by the 
BinaryMarshaller.
-                final Boolean locMarshStrSerialVer2 = 
locNode.attribute(ATTR_MARSHALLER_USE_BINARY_STRING_SER_VER_2);
-                final boolean locMarshStrSerialVer2Bool = 
locMarshStrSerialVer2 != null ? locMarshStrSerialVer2 : false;
+            if (locMarshCompactFooter == rmtMarshCompactFooter)
+                return null;
 
-                final Boolean rmtMarshStrSerialVer2 = 
node.attribute(ATTR_MARSHALLER_USE_BINARY_STRING_SER_VER_2);
-                final boolean rmtMarshStrSerialVer2Bool = 
rmtMarshStrSerialVer2 != null ? rmtMarshStrSerialVer2 : false;
+            String errMsg = "Local node's binary marshaller \"compactFooter\" 
property differs from " +
+                "the same property on remote node (make sure all nodes in 
topology have the same value " +
+                "of \"compactFooter\" property) [locMarshallerCompactFooter=" 
+ locMarshCompactFooter +
+                ", rmtMarshallerCompactFooter=" + rmtMarshCompactFooter +
+                ", locNodeAddrs=" + U.addressesAsString(locNode) +
+                ", rmtNodeAddrs=" + U.addressesAsString(req.node()) +
+                ", locNodeId=" + locNode.id() + ", rmtNodeId=" + 
req.creatorNodeId() + ']';
 
-                if (locMarshStrSerialVer2Bool != rmtMarshStrSerialVer2Bool) {
-                    utilityPool.execute(
-                        new Runnable() {
-                            @Override public void run() {
-                                String errMsg = "Local node's " + 
IGNITE_BINARY_MARSHALLER_USE_STRING_SERIALIZATION_VER_2 +
-                                    " property value differs from remote 
node's value " +
-                                    "(to make sure all nodes in topology have 
identical marshaller settings, " +
-                                    "configure system property explicitly) " +
-                                    "[locMarshStrSerialVer2=" + 
locMarshStrSerialVer2 +
-                                    ", rmtMarshStrSerialVer2=" + 
rmtMarshStrSerialVer2 +
-                                    ", locNodeAddrs=" + 
U.addressesAsString(locNode) +
-                                    ", rmtNodeAddrs=" + 
U.addressesAsString(node) +
-                                    ", locNodeId=" + locNode.id() + ", 
rmtNodeId=" + msg.creatorNodeId() + ']';
-
-                                String sndMsg = "Local node's " + 
IGNITE_BINARY_MARSHALLER_USE_STRING_SERIALIZATION_VER_2 +
-                                    " property value differs from remote 
node's value " +
-                                    "(to make sure all nodes in topology have 
identical marshaller settings, " +
-                                    "configure system property explicitly) " +
-                                    "[locMarshStrSerialVer2=" + 
rmtMarshStrSerialVer2 +
-                                    ", rmtMarshStrSerialVer2=" + 
locMarshStrSerialVer2 +
-                                    ", locNodeAddrs=" + 
U.addressesAsString(node) + ", locPort=" + node.discoveryPort() +
-                                    ", rmtNodeAddr=" + 
U.addressesAsString(locNode) + ", locNodeId=" + node.id() +
-                                    ", rmtNodeId=" + locNode.id() + ']';
-
-                                nodeCheckError(
-                                    node,
-                                    errMsg,
-                                    sndMsg);
-                            }
-                        });
+            String sndMsg = "Local node's binary marshaller \"compactFooter\" 
property differs from " +
+                "the same property on remote node (make sure all nodes in 
topology have the same value " +
+                "of \"compactFooter\" property) [locMarshallerCompactFooter=" 
+ rmtMarshCompactFooter +
+                ", rmtMarshallerCompactFooter=" + locMarshCompactFooter +
+                ", locNodeAddrs=" + U.addressesAsString(req.node()) + ", 
locPort=" + req.node().discoveryPort() +
+                ", rmtNodeAddr=" + U.addressesAsString(locNode) + ", 
locNodeId=" + req.node().id() +
+                ", rmtNodeId=" + locNode.id() + ']';
 
-                    // Ignore join request.
-                    msg.spanContainer().span()
-                        .addLog(() -> "Ignored")
-                        .setStatus(SpanStatus.ABORTED)
-                        .end();
+            return new IgniteNodeValidationResult(req.node().id(), errMsg, 
sndMsg);
+        }
 
-                    return;
-                }
+        /** */
+        private IgniteNodeValidationResult 
validateStringSerializationVersion(TcpDiscoveryJoinRequestMessage req) {
+            boolean locMarshStrSerialVer2 = booleanAttribute(locNode, 
ATTR_MARSHALLER_USE_BINARY_STRING_SER_VER_2, false);
+            boolean rmtMarshStrSerialVer2 = booleanAttribute(req.node(), 
ATTR_MARSHALLER_USE_BINARY_STRING_SER_VER_2, false);
 
-                Boolean rmtLateAssign = 
node.attribute(ATTR_LATE_AFFINITY_ASSIGNMENT);
-                // Can be null only in tests.
-                boolean rmtLateAssignBool = rmtLateAssign != null ? 
rmtLateAssign : false;
+            if (locMarshStrSerialVer2 == rmtMarshStrSerialVer2)
+                return null;
 
-                if (locLateAssignBool != rmtLateAssignBool) {
-                    String errMsg = "Local node's cache affinity assignment 
mode differs from " +
-                        "the same property on remote node (make sure all nodes 
in topology have the same " +
-                        "cache affinity assignment mode) [locLateAssign=" + 
locLateAssignBool +
-                        ", rmtLateAssign=" + rmtLateAssignBool +
-                        ", locNodeAddrs=" + U.addressesAsString(locNode) +
-                        ", rmtNodeAddrs=" + U.addressesAsString(node) +
-                        ", locNodeId=" + locNode.id() + ", rmtNodeId=" + 
msg.creatorNodeId() + ']';
+            String errMsg = "Local node's " + 
IGNITE_BINARY_MARSHALLER_USE_STRING_SERIALIZATION_VER_2 +
+                " property value differs from remote node's value " +
+                "(to make sure all nodes in topology have identical marshaller 
settings, " +
+                "configure system property explicitly) " +
+                "[locMarshStrSerialVer2=" + locMarshStrSerialVer2 +
+                ", rmtMarshStrSerialVer2=" + rmtMarshStrSerialVer2 +
+                ", locNodeAddrs=" + U.addressesAsString(locNode) +
+                ", rmtNodeAddrs=" + U.addressesAsString(req.node()) +
+                ", locNodeId=" + locNode.id() + ", rmtNodeId=" + 
req.creatorNodeId() + ']';
+
+            String sndMsg = "Local node's " + 
IGNITE_BINARY_MARSHALLER_USE_STRING_SERIALIZATION_VER_2 +
+                " property value differs from remote node's value " +
+                "(to make sure all nodes in topology have identical marshaller 
settings, " +
+                "configure system property explicitly) " +
+                "[locMarshStrSerialVer2=" + rmtMarshStrSerialVer2 +
+                ", rmtMarshStrSerialVer2=" + locMarshStrSerialVer2 +
+                ", locNodeAddrs=" + U.addressesAsString(req.node()) + ", 
locPort=" + req.node().discoveryPort() +
+                ", rmtNodeAddr=" + U.addressesAsString(locNode) + ", 
locNodeId=" + req.node().id() +
+                ", rmtNodeId=" + locNode.id() + ']';
+
+            return new IgniteNodeValidationResult(req.node().id(), errMsg, 
sndMsg);
+        }
 
-                    String sndMsg = "Local node's cache affinity assignment 
mode differs from " +
-                        "the same property on remote node (make sure all nodes 
in topology have the same " +
-                        "cache affinity assignment mode) [locLateAssign=" + 
rmtLateAssignBool +
-                        ", rmtLateAssign=" + locLateAssign +
-                        ", locNodeAddrs=" + U.addressesAsString(node) + ", 
locPort=" + node.discoveryPort() +
-                        ", rmtNodeAddr=" + U.addressesAsString(locNode) + ", 
locNodeId=" + node.id() +
-                        ", rmtNodeId=" + locNode.id() + ']';
+        /** */
+        private IgniteNodeValidationResult 
validateLateAffinityAssignment(TcpDiscoveryJoinRequestMessage req) {
+            boolean locLateAssign = booleanAttribute(locNode, 
ATTR_LATE_AFFINITY_ASSIGNMENT, false);
+            boolean rmtLateAssign = booleanAttribute(req.node(), 
ATTR_LATE_AFFINITY_ASSIGNMENT, false);
 
-                    nodeCheckError(node, errMsg, sndMsg);
+            if (locLateAssign == rmtLateAssign)
+                return null;
 
-                    // Ignore join request.
-                    msg.spanContainer().span()
-                        .addLog(() -> "Ignored")
-                        .setStatus(SpanStatus.ABORTED)
-                        .end();
+            String errMsg = "Local node's cache affinity assignment mode 
differs from " +
+                "the same property on remote node (make sure all nodes in 
topology have the same " +
+                "cache affinity assignment mode) [locLateAssign=" + 
locLateAssign +
+                ", rmtLateAssign=" + rmtLateAssign +
+                ", locNodeAddrs=" + U.addressesAsString(locNode) +
+                ", rmtNodeAddrs=" + U.addressesAsString(req.node()) +
+                ", locNodeId=" + locNode.id() + ", rmtNodeId=" + 
req.creatorNodeId() + ']';
 
-                    return;
-                }
+            String sndMsg = "Local node's cache affinity assignment mode 
differs from " +
+                "the same property on remote node (make sure all nodes in 
topology have the same " +
+                "cache affinity assignment mode) [locLateAssign=" + 
rmtLateAssign +
+                ", rmtLateAssign=" + locLateAssign +
+                ", locNodeAddrs=" + U.addressesAsString(req.node()) + ", 
locPort=" + req.node().discoveryPort() +
+                ", rmtNodeAddr=" + U.addressesAsString(locNode) + ", 
locNodeId=" + req.node().id() +
+                ", rmtNodeId=" + locNode.id() + ']';
 
-                if (!node.isClient()) {
-                    String locNodeDcId = locNode.dataCenterId();
-                    String rmtNodeDcId = node.dataCenterId();
-
-                    if (locNodeDcId == null && rmtNodeDcId != null
-                        || locNodeDcId != null && rmtNodeDcId == null) {
-                        utilityPool.execute(
-                            new Runnable() {
-                                @Override public void run() {
-                                    String locNodeHasDcId = "Data Center ID is 
specified for local node but not for remote node";
-                                    String rmtNodeHasDcId = "Data Center ID is 
specified for remote node but not for local node";
-
-                                    String errMsg = locNodeDcId == null ? 
locNodeHasDcId : rmtNodeHasDcId +
-                                        "[locNodeDcId=" + locNodeDcId +
-                                        ", rmtNodeDcId=" + rmtNodeDcId +
-                                        ", locNodeAddrs=" + 
U.addressesAsString(locNode) +
-                                        ", rmtNodeAddrs=" + 
U.addressesAsString(node) +
-                                        ", locNodeId=" + locNode.id() + ", 
rmtNodeId=" + msg.creatorNodeId() + ']';
-
-                                    String sndMsg = rmtNodeDcId == null ? 
rmtNodeHasDcId : locNodeHasDcId +
-                                        "[locNodeDcId=" + rmtNodeDcId +
-                                        ", rmtNodeDcId=" + locNodeDcId +
-                                        ", locNodeAddrs=" + 
U.addressesAsString(node) + ", locPort=" + node.discoveryPort() +
-                                        ", rmtNodeAddr=" + 
U.addressesAsString(locNode) + ", locNodeId=" + node.id() +
-                                        ", rmtNodeId=" + locNode.id() + ']';
-
-                                    nodeCheckError(
-                                        node,
-                                        errMsg,
-                                        sndMsg);
-                                }
-                            });
+            return new IgniteNodeValidationResult(req.node().id(), errMsg, 
sndMsg);
+        }
 
-                        // Ignore join request.
-                        msg.spanContainer().span()
-                            .addLog(() -> "Ignored")
-                            .setStatus(SpanStatus.ABORTED)
-                            .end();
+        /** */
+        private IgniteNodeValidationResult 
validateDataCenterId(TcpDiscoveryJoinRequestMessage req) {
+            if (req.node().isClient())
+                return null;
 
-                        return;
-                    }
-                }
+            String locNodeDcId = locNode.dataCenterId();
+            String rmtNodeDcId = req.node().dataCenterId();
 
-                // Handle join.
-                node.internalOrder(ring.nextNodeOrder());
+            if (locNodeDcId == null && rmtNodeDcId == null || locNodeDcId != 
null && rmtNodeDcId != null)
+                return null;
 
-                if (log.isDebugEnabled())
-                    log.debug("Internal order has been assigned to node: " + 
node);
+            String locNodeHasDcId = "Data Center ID is specified for local 
node but not for remote node";
+            String rmtNodeHasDcId = "Data Center ID is specified for remote 
node but not for local node";
 
-                DiscoveryDataPacket data = msg.gridDiscoveryData();
+            String errMsg = locNodeDcId == null ? locNodeHasDcId : 
rmtNodeHasDcId +
+                "[locNodeDcId=" + locNodeDcId +
+                ", rmtNodeDcId=" + rmtNodeDcId +
+                ", locNodeAddrs=" + U.addressesAsString(locNode) +
+                ", rmtNodeAddrs=" + U.addressesAsString(req.node()) +
+                ", locNodeId=" + locNode.id() + ", rmtNodeId=" + 
req.creatorNodeId() + ']';
 
-                TcpDiscoveryNodeAddedMessage nodeAddedMsg = new 
TcpDiscoveryNodeAddedMessage(locNodeId,
-                    node, data, spi.gridStartTime);
+            String sndMsg = rmtNodeDcId == null ? rmtNodeHasDcId : 
locNodeHasDcId +
+                "[locNodeDcId=" + rmtNodeDcId +
+                ", rmtNodeDcId=" + locNodeDcId +
+                ", locNodeAddrs=" + U.addressesAsString(req.node()) + ", 
locPort=" + req.node().discoveryPort() +
+                ", rmtNodeAddr=" + U.addressesAsString(locNode) + ", 
locNodeId=" + req.node().id() +
+                ", rmtNodeId=" + locNode.id() + ']';
 
-                nodeAddedMsg = tracing.messages().branch(nodeAddedMsg, msg);
+            return new IgniteNodeValidationResult(req.node().id(), errMsg, 
sndMsg);
+        }
 
-                nodeAddedMsg.client(msg.client());
+        /** */
+        private IgniteNodeValidationResult 
validateByIgniteComponents(TcpDiscoveryJoinRequestMessage req) {
+            return spi.getSpiContext().validateNode(req.node());
+        }
 
-                processNodeAddedMessage(nodeAddedMsg);
+        /** */
+        private IgniteNodeValidationResult 
validateByIgniteComponentsWithJoiningNodeData(TcpDiscoveryJoinRequestMessage 
req) {
+            DiscoveryDataBag data = 
req.gridDiscoveryData().bagWithJoiningNodeData();
 
-                tracing.messages().finishProcessing(nodeAddedMsg);
-            }
-            else {
-                if (sendMessageToRemotes(msg))
-                    sendMessageAcrossRing(msg);
-            }
+            return spi.getSpiContext().validateNode(req.node(), data);
         }
 
-        /**
-         * @param node Node.
-         * @param name Attribute name.
-         * @param dflt Default value.
-         * @return Attribute value.
-         */
-        private boolean booleanAttribute(ClusterNode node, String name, 
boolean dflt) {
-            Boolean attr = node.attribute(name);
+        /** */
+        private IgniteNodeValidationResult 
validateJoiningNode(TcpDiscoveryJoinRequestMessage msg) {
+            for (Function<TcpDiscoveryJoinRequestMessage, 
IgniteNodeValidationResult> validator : nodeValidators) {
+                IgniteNodeValidationResult validationRes = 
validator.apply(msg);
 
-            return attr != null ? attr : dflt;
+                if (validationRes != null)
+                    return validationRes;
+            }
+
+            return null;
         }
 
-        /**
-         * @param node Joining node.
-         * @param errMsg Message to log.
-         * @param sndMsg Message to send.
-         */
-        private void nodeCheckError(TcpDiscoveryNode node, String errMsg, 
String sndMsg) {
-            LT.warn(log, errMsg);
+        /** */
+        private void 
sendJoiningNodeCheckFailedResponse(TcpDiscoveryJoinRequestMessage req, 
IgniteNodeValidationResult validationErr) {
+            utilityPool.execute(() -> {
+                TcpDiscoveryNode node = req.node();
 
-            // Always output in debug.
-            if (log.isDebugEnabled())
-                log.debug(errMsg);
+                spi.getSpiContext().recordEvent(new 
NodeValidationFailedEvent(locNode, node, validationErr));
 
-            try {
-                trySendMessageDirectly(node, new 
TcpDiscoveryCheckFailedMessage(locNode.id(), sndMsg));
-            }
-            catch (IgniteSpiException e) {
+                boolean ping = node.id().equals(validationErr.nodeId()) ? 
pingNode(node) : pingNode(validationErr.nodeId());
+
+                if (!ping) {
+                    if (log.isDebugEnabled()) {
+                        log.debug("Conflicting node has already left, need to 
wait for event. " +
+                            "Will ignore join request for now since it will be 
recent [req=" + req +
+                            ", err=" + validationErr.message() + ']');
+                    }
+
+                    return;
+                }
+
+                LT.warn(log, validationErr.message());
+
+                // Always output in debug.
                 if (log.isDebugEnabled())
-                    log.debug("Failed to send marshaller check failed message 
to node " +
-                        "[node=" + node + ", err=" + e.getMessage() + ']');
+                    log.debug(validationErr.message());
 
-                onException("Failed to send marshaller check failed message to 
node " +
-                    "[node=" + node + ", err=" + e.getMessage() + ']', e);
-            }
+                try {
+                    trySendMessageDirectly(node, new 
TcpDiscoveryCheckFailedMessage(node.id(), validationErr.sendMessage()));
+                }
+                catch (IgniteSpiException e) {
+                    if (log.isDebugEnabled())
+                        log.debug("Failed to send check failed message to node 
" +
+                            "[node=" + node + ", err=" + e.getMessage() + ", 
checkErr " + validationErr.message() + ']');
+
+                    onException("Failed to send check failed message to node " 
+
+                        "[node=" + node + ", err=" + e.getMessage() + ", 
checkErr" + validationErr.message() + ']', e);
+                }
+            });
         }
 
         /** */
diff --git 
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java
 
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java
index f634b44f173..a7f68fb1cf5 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java
@@ -97,7 +97,6 @@ import 
org.apache.ignite.spi.discovery.DiscoverySpiNodeAuthenticator;
 import org.apache.ignite.spi.discovery.DiscoverySpiOrderSupport;
 import org.apache.ignite.spi.discovery.tcp.internal.DiscoveryDataPacket;
 import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNode;
-import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNodesRing;
 import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryStatistics;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
 import 
org.apache.ignite.spi.discovery.tcp.ipfinder.jdbc.TcpDiscoveryJdbcIpFinder;
@@ -520,16 +519,6 @@ public class TcpDiscoverySpi extends IgniteSpiAdapter 
implements IgniteDiscovery
         return getNode(id);
     }
 
-    /**
-     * @return TCP discovery nodes ring.
-     */
-    @Nullable public TcpDiscoveryNodesRing discoveryRing() {
-        if (impl instanceof ServerImpl)
-            return ((ServerImpl)impl).ring();
-
-        return null;
-    }
-
     /** {@inheritDoc} */
     @Override public boolean pingNode(UUID nodeId) {
         return impl.pingNode(nodeId);
diff --git 
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryNodesRing.java
 
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryNodesRing.java
index 7752994cf00..5cafd170732 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryNodesRing.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryNodesRing.java
@@ -31,7 +31,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
 import org.apache.ignite.IgniteSystemProperties;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.internal.util.lang.ClusterNodeFunc;
-import org.apache.ignite.internal.util.lang.IgnitePair;
 import org.apache.ignite.internal.util.tostring.GridToStringExclude;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
 import org.apache.ignite.internal.util.typedef.F;
@@ -40,7 +39,6 @@ import org.apache.ignite.internal.util.typedef.PN;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgnitePredicate;
-import org.apache.ignite.lang.IgniteProductVersion;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -96,24 +94,6 @@ public class TcpDiscoveryNodesRing {
     @GridToStringExclude
     private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
 
-    /** Minimum node version in the cluster. */
-    private IgniteProductVersion minNodeVer;
-
-    /** Maximum node version in the cluster. */
-    private IgniteProductVersion maxNodeVer;
-
-    /** Returns min and max node versions. */
-    public IgnitePair<IgniteProductVersion> minMaxNodeVersions() {
-        rwLock.readLock().lock();
-
-        try {
-            return F.pair(minNodeVer, maxNodeVer);
-        }
-        finally {
-            rwLock.readLock().unlock();
-        }
-    }
-
     /**
      * Sets local node.
      *
@@ -262,8 +242,6 @@ public class TcpDiscoveryNodesRing {
             nodeOrder = node.internalOrder();
 
             maxInternalOrder = node.internalOrder();
-
-            initializeMinMaxVersions();
         }
         finally {
             rwLock.writeLock().unlock();
@@ -334,8 +312,6 @@ public class TcpDiscoveryNodesRing {
             }
 
             nodeOrder = topVer;
-
-            initializeMinMaxVersions();
         }
         finally {
             rwLock.writeLock().unlock();
@@ -382,8 +358,6 @@ public class TcpDiscoveryNodesRing {
                 nodes.remove(rmv);
             }
 
-            initializeMinMaxVersions();
-
             return rmv;
         }
         finally {
@@ -415,11 +389,6 @@ public class TcpDiscoveryNodesRing {
             maxInternalOrder = 0;
 
             topVer = 0;
-
-            if (locNode != null) {
-                minNodeVer = locNode.version();
-                maxNodeVer = locNode.version();
-            }
         }
         finally {
             rwLock.writeLock().unlock();
@@ -727,22 +696,6 @@ public class TcpDiscoveryNodesRing {
         });
     }
 
-    /**
-     *
-     */
-    private void initializeMinMaxVersions() {
-        minNodeVer = null;
-        maxNodeVer = null;
-
-        for (TcpDiscoveryNode node : nodes) {
-            if (minNodeVer == null || node.version().compareTo(minNodeVer) < 0)
-                minNodeVer = node.version();
-
-            if (maxNodeVer == null || node.version().compareTo(maxNodeVer) > 0)
-                maxNodeVer = node.version();
-        }
-    }
-
     /** {@inheritDoc} */
     @Override public String toString() {
         rwLock.readLock().lock();
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/GridReleaseTypeSelfTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/GridReleaseTypeSelfTest.java
index 853f330c107..bc9771858de 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/GridReleaseTypeSelfTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/GridReleaseTypeSelfTest.java
@@ -243,10 +243,6 @@ public class GridReleaseTypeSelfTest extends 
GridCommonAbstractTest {
 
         assertTrue(X.hasCause(e, "Local node's marshaller differs from remote 
node's marshaller", IgniteSpiException.class));
 
-        assertDisablingFails(ign0, "Can't disable rolling upgrade with 
different versions in cluster");
-
-        doSleep(joinTimeout * 3);
-
         ign0.context().rollingUpgrade().disable();
 
         assertFalse(ign0.context().rollingUpgrade().enabled());
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/IgniteNodeValidationFailedEventTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/IgniteNodeValidationFailedEventTest.java
index 9bd2dc149a8..97ad9ee70df 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/IgniteNodeValidationFailedEventTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/IgniteNodeValidationFailedEventTest.java
@@ -38,7 +38,6 @@ public class IgniteNodeValidationFailedEventTest extends 
GridCommonAbstractTest
     /** {@inheritDoc} */
     @Override protected IgniteConfiguration getConfiguration(String 
igniteInstanceName) throws Exception {
         return super.getConfiguration(igniteInstanceName)
-            .setIncludeEventTypes(EVT_NODE_VALIDATION_FAILED)
             .setConsistentId(igniteInstanceName);
     }
 
@@ -98,9 +97,9 @@ public class IgniteNodeValidationFailedEventTest extends 
GridCommonAbstractTest
 
     /** */
     @Test
-    public void testEventDisabledByDefault() throws Exception {
+    public void testEventEnabledByDefault() throws Exception {
         IgniteEx ignite = 
startGrid(super.getConfiguration(getTestIgniteInstanceName(0)));
 
-        
assertFalse(ignite.context().event().isRecordable(EVT_NODE_VALIDATION_FAILED));
+        
assertTrue(ignite.context().event().isRecordable(EVT_NODE_VALIDATION_FAILED));
     }
 }

Reply via email to