You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ib...@apache.org on 2023/01/30 13:38:10 UTC

[ignite-3] branch main updated: IGNITE-16785 Add configuration for the timeout for removing nodes from logical topology (#1597)

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

ibessonov 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 5fb4ae65db IGNITE-16785 Add configuration for the timeout for removing nodes from logical topology (#1597)
5fb4ae65db is described below

commit 5fb4ae65db7c02de81e9c67d3850bd95510970a7
Author: Roman Puchkovskiy <ro...@gmail.com>
AuthorDate: Mon Jan 30 17:38:05 2023 +0400

    IGNITE-16785 Add configuration for the timeout for removing nodes from logical topology (#1597)
---
 .../repl/executor/ItIgnitePicocliCommandsTest.java |  4 +-
 modules/cluster-management/build.gradle            |  5 +++
 .../internal/cluster/management/MockNode.java      | 20 ++++++++-
 .../management/raft/ItCmgRaftServiceTest.java      | 11 ++++-
 .../management/ClusterManagementGroupManager.java  | 24 ++++++-----
 .../ClusterManagementConfigurationModule.java      | 41 ++++++++++++++++++
 .../ClusterManagementConfigurationSchema.java      | 48 ++++++++++++++++++++++
 .../management/raft/CmgRaftGroupListener.java      | 11 ++++-
 .../cluster/management/raft/ValidationManager.java |  9 ++--
 .../management/raft/CmgRaftGroupListenerTest.java  | 12 +++++-
 .../ItDistributedConfigurationPropertiesTest.java  |  7 +++-
 .../ItDistributedConfigurationStorageTest.java     |  7 +++-
 .../storage/ItRebalanceDistributedTest.java        |  7 +++-
 .../runner/app/ItIgniteNodeRestartTest.java        |  7 +++-
 .../org/apache/ignite/internal/app/IgniteImpl.java | 26 +++++++-----
 15 files changed, 204 insertions(+), 35 deletions(-)

diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/core/repl/executor/ItIgnitePicocliCommandsTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/core/repl/executor/ItIgnitePicocliCommandsTest.java
index c83347b204..72aaa3d807 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/core/repl/executor/ItIgnitePicocliCommandsTest.java
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/core/repl/executor/ItIgnitePicocliCommandsTest.java
@@ -216,7 +216,7 @@ public class ItIgnitePicocliCommandsTest extends CliCommandTestInitializedIntegr
         // wait for lazy init of node config completer
         await("For given parsed words: " + givenParsedLine.words()).until(
                 () -> complete(givenParsedLine),
-                containsInAnyOrder("rest", "compute", "clientConnector", "raft", "network")
+                containsInAnyOrder("rest", "compute", "clientConnector", "raft", "network", "cluster")
         );
     }
 
@@ -243,7 +243,7 @@ public class ItIgnitePicocliCommandsTest extends CliCommandTestInitializedIntegr
         // wait for lazy init of node config completer
         await("For given parsed words: " + givenParsedLine.words()).until(
                 () -> complete(givenParsedLine),
-                containsInAnyOrder("rest", "clientConnector", "network")
+                containsInAnyOrder("rest", "clientConnector", "network", "cluster")
         );
     }
 
diff --git a/modules/cluster-management/build.gradle b/modules/cluster-management/build.gradle
index 85e5fc54ee..f2b3cb5e65 100644
--- a/modules/cluster-management/build.gradle
+++ b/modules/cluster-management/build.gradle
@@ -24,10 +24,13 @@ apply from: "$rootDir/buildscripts/java-integration-test.gradle"
 description = 'ignite-cluster-management'
 
 dependencies {
+    annotationProcessor project(':ignite-configuration-annotation-processor')
     annotationProcessor project(":ignite-network-annotation-processor")
     annotationProcessor libs.micronaut.inject.annotation.processor
+    annotationProcessor libs.auto.service
 
     implementation project(':ignite-core')
+    implementation project(':ignite-configuration')
     implementation project(':ignite-network')
     implementation project(':ignite-raft-api')
     implementation project(':ignite-vault')
@@ -37,10 +40,12 @@ dependencies {
     implementation libs.micronaut.http.core
     implementation libs.micronaut.http.server.core
     implementation libs.fastutil.core
+    implementation libs.auto.service.annotations
 
     testImplementation project(':ignite-configuration')
     testImplementation project(':ignite-core')
     testImplementation(testFixtures(project(':ignite-core')))
+    testImplementation(testFixtures(project(':ignite-configuration')))
     testImplementation project(':ignite-network')
     testImplementation libs.hamcrest.core
     testImplementation libs.mockito.junit
diff --git a/modules/cluster-management/src/integrationTest/java/org/apache/ignite/internal/cluster/management/MockNode.java b/modules/cluster-management/src/integrationTest/java/org/apache/ignite/internal/cluster/management/MockNode.java
index 3e8dd255eb..dee7fdcbe1 100644
--- a/modules/cluster-management/src/integrationTest/java/org/apache/ignite/internal/cluster/management/MockNode.java
+++ b/modules/cluster-management/src/integrationTest/java/org/apache/ignite/internal/cluster/management/MockNode.java
@@ -28,7 +28,9 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
 import org.apache.ignite.configuration.ConfigurationValue;
+import org.apache.ignite.internal.cluster.management.configuration.ClusterManagementConfiguration;
 import org.apache.ignite.internal.cluster.management.raft.RocksDbClusterStateStorage;
 import org.apache.ignite.internal.cluster.management.topology.LogicalTopologyImpl;
 import org.apache.ignite.internal.cluster.management.topology.api.LogicalTopologySnapshot;
@@ -88,6 +90,21 @@ public class MockNode {
         when(raftConfiguration.rpcInstallSnapshotTimeout()).thenReturn(rpcInstallSnapshotTimeoutValue);
         when(rpcInstallSnapshotTimeoutValue.value()).thenReturn(10);
 
+        ClusterManagementConfiguration clusterManagementConfiguration = mock(ClusterManagementConfiguration.class);
+        ConfigurationValue<Long> failoverTimeoutValue = mock(ConfigurationValue.class);
+        ConfigurationValue<Long> networkInvokeTimeoutValue = mock(ConfigurationValue.class);
+        ConfigurationValue<Long> incompleteJoinTimeoutValue = mock(ConfigurationValue.class);
+
+        when(clusterManagementConfiguration.failoverTimeout())
+                .thenReturn(failoverTimeoutValue);
+        when(failoverTimeoutValue.value()).thenReturn(0L);
+
+        when(clusterManagementConfiguration.networkInvokeTimeout()).thenReturn(networkInvokeTimeoutValue);
+        when(networkInvokeTimeoutValue.value()).thenReturn(500L);
+
+        when(clusterManagementConfiguration.incompleteJoinTimeout()).thenReturn(incompleteJoinTimeoutValue);
+        when(incompleteJoinTimeoutValue.value()).thenReturn(TimeUnit.HOURS.toMillis(1));
+
         Loza raftManager = new Loza(clusterService, raftConfiguration, workDir, new HybridClockImpl());
 
         var clusterStateStorage = new RocksDbClusterStateStorage(workDir.resolve("cmg"));
@@ -99,7 +116,8 @@ public class MockNode {
                 clusterService,
                 raftManager,
                 clusterStateStorage,
-                logicalTopologyService
+                logicalTopologyService,
+                clusterManagementConfiguration
         );
 
         components.add(vaultManager);
diff --git a/modules/cluster-management/src/integrationTest/java/org/apache/ignite/internal/cluster/management/raft/ItCmgRaftServiceTest.java b/modules/cluster-management/src/integrationTest/java/org/apache/ignite/internal/cluster/management/raft/ItCmgRaftServiceTest.java
index 8ee84e07b3..03d444d9c9 100644
--- a/modules/cluster-management/src/integrationTest/java/org/apache/ignite/internal/cluster/management/raft/ItCmgRaftServiceTest.java
+++ b/modules/cluster-management/src/integrationTest/java/org/apache/ignite/internal/cluster/management/raft/ItCmgRaftServiceTest.java
@@ -43,6 +43,7 @@ import java.util.concurrent.TimeUnit;
 import org.apache.ignite.internal.cluster.management.ClusterState;
 import org.apache.ignite.internal.cluster.management.ClusterTag;
 import org.apache.ignite.internal.cluster.management.CmgGroupId;
+import org.apache.ignite.internal.cluster.management.configuration.ClusterManagementConfiguration;
 import org.apache.ignite.internal.cluster.management.network.messages.CmgMessagesFactory;
 import org.apache.ignite.internal.cluster.management.raft.commands.JoinReadyCommand;
 import org.apache.ignite.internal.cluster.management.raft.commands.JoinRequestCommand;
@@ -86,6 +87,9 @@ public class ItCmgRaftServiceTest {
     @InjectConfiguration
     private static RaftConfiguration raftConfiguration;
 
+    @InjectConfiguration
+    private static ClusterManagementConfiguration clusterManagementConfiguration;
+
     private final CmgMessagesFactory msgFactory = new CmgMessagesFactory();
 
     private class Node {
@@ -130,7 +134,12 @@ public class ItCmgRaftServiceTest {
                     raftService = raftManager.startRaftGroupNode(
                             new RaftNodeId(CmgGroupId.INSTANCE, serverPeer),
                             configuration,
-                            new CmgRaftGroupListener(raftStorage, new LogicalTopologyImpl(raftStorage), term -> {}),
+                            new CmgRaftGroupListener(
+                                    raftStorage,
+                                    new LogicalTopologyImpl(raftStorage),
+                                    clusterManagementConfiguration,
+                                    term -> {}
+                            ),
                             RaftGroupEventsListener.noopLsnr
                     );
                 }
diff --git a/modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/ClusterManagementGroupManager.java b/modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/ClusterManagementGroupManager.java
index ca9c932930..70767928da 100644
--- a/modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/ClusterManagementGroupManager.java
+++ b/modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/ClusterManagementGroupManager.java
@@ -36,6 +36,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Function;
 import org.apache.ignite.internal.cluster.management.LocalStateStorage.LocalState;
+import org.apache.ignite.internal.cluster.management.configuration.ClusterManagementConfiguration;
 import org.apache.ignite.internal.cluster.management.network.CmgMessageHandlerFactory;
 import org.apache.ignite.internal.cluster.management.network.messages.CancelInitMessage;
 import org.apache.ignite.internal.cluster.management.network.messages.ClusterStateMessage;
@@ -83,9 +84,6 @@ import org.jetbrains.annotations.TestOnly;
  * for the description of the Cluster Management Group and its responsibilities.
  */
 public class ClusterManagementGroupManager implements IgniteComponent {
-    // TODO: timeout should be configurable, see https://issues.apache.org/jira/browse/IGNITE-16785
-    private static final int NETWORK_INVOKE_TIMEOUT = 500;
-
     private static final IgniteLogger LOG = Loggers.forClass(ClusterManagementGroupManager.class);
 
     /** Busy lock to stop synchronously. */
@@ -121,6 +119,8 @@ public class ClusterManagementGroupManager implements IgniteComponent {
 
     private final LogicalTopology logicalTopology;
 
+    private final ClusterManagementConfiguration configuration;
+
     /** Local state. */
     private final LocalStateStorage localStateStorage;
 
@@ -147,12 +147,15 @@ public class ClusterManagementGroupManager implements IgniteComponent {
             ClusterService clusterService,
             RaftManager raftManager,
             ClusterStateStorage clusterStateStorage,
-            LogicalTopology logicalTopology
+            LogicalTopology logicalTopology,
+            ClusterManagementConfiguration configuration
     ) {
         this.clusterService = clusterService;
         this.raftManager = raftManager;
         this.clusterStateStorage = clusterStateStorage;
         this.logicalTopology = logicalTopology;
+        this.configuration = configuration;
+
         this.localStateStorage = new LocalStateStorage(vault);
         this.clusterInitializer = new ClusterInitializer(clusterService);
     }
@@ -506,9 +509,9 @@ public class ClusterManagementGroupManager implements IgniteComponent {
 
         Set<String> learnerNames = isLearner ? Set.of(thisNodeConsistentId) : Set.of();
 
-        PeersAndLearners configuration = PeersAndLearners.fromConsistentIds(nodeNames, learnerNames);
+        PeersAndLearners raftConfiguration = PeersAndLearners.fromConsistentIds(nodeNames, learnerNames);
 
-        Peer serverPeer = isLearner ? configuration.learner(thisNodeConsistentId) : configuration.peer(thisNodeConsistentId);
+        Peer serverPeer = isLearner ? raftConfiguration.learner(thisNodeConsistentId) : raftConfiguration.peer(thisNodeConsistentId);
 
         assert serverPeer != null;
 
@@ -516,8 +519,8 @@ public class ClusterManagementGroupManager implements IgniteComponent {
             return raftManager
                     .startRaftGroupNode(
                             new RaftNodeId(CmgGroupId.INSTANCE, serverPeer),
-                            configuration,
-                            new CmgRaftGroupListener(clusterStateStorage, logicalTopology, this::onLogicalTopologyChanged),
+                            raftConfiguration,
+                            new CmgRaftGroupListener(clusterStateStorage, logicalTopology, configuration, this::onLogicalTopologyChanged),
                             createCmgRaftGroupEventsListener()
                     )
                     .thenApply(service -> new CmgRaftService(service, clusterService, logicalTopology));
@@ -597,14 +600,13 @@ public class ClusterManagementGroupManager implements IgniteComponent {
     }
 
     private void scheduleRemoveFromLogicalTopology(CmgRaftService raftService, ClusterNode node) {
-        // TODO: delay should be configurable, see https://issues.apache.org/jira/browse/IGNITE-16785
         scheduledExecutor.schedule(() -> {
             ClusterNode physicalTopologyNode = clusterService.topologyService().getByConsistentId(node.name());
 
             if (physicalTopologyNode == null || !physicalTopologyNode.id().equals(node.id())) {
                 raftService.removeFromCluster(Set.of(node));
             }
-        }, 0, TimeUnit.MILLISECONDS);
+        }, configuration.failoverTimeout().value(), TimeUnit.MILLISECONDS);
     }
 
     private void sendClusterState(CmgRaftService raftService, ClusterNode node) {
@@ -648,7 +650,7 @@ public class ClusterManagementGroupManager implements IgniteComponent {
     }
 
     private void sendWithRetry(ClusterNode node, NetworkMessage msg, CompletableFuture<Void> result, int attempts) {
-        clusterService.messagingService().invoke(node, msg, NETWORK_INVOKE_TIMEOUT)
+        clusterService.messagingService().invoke(node, msg, configuration.networkInvokeTimeout().value())
                 .whenComplete((response, e) -> {
                     if (e == null) {
                         result.complete(null);
diff --git a/modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/configuration/ClusterManagementConfigurationModule.java b/modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/configuration/ClusterManagementConfigurationModule.java
new file mode 100644
index 0000000000..bddeba043c
--- /dev/null
+++ b/modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/configuration/ClusterManagementConfigurationModule.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cluster.management.configuration;
+
+import com.google.auto.service.AutoService;
+import java.util.Collection;
+import java.util.List;
+import org.apache.ignite.configuration.RootKey;
+import org.apache.ignite.configuration.annotation.ConfigurationType;
+import org.apache.ignite.internal.configuration.ConfigurationModule;
+
+/**
+ * Configuration module for Cluster Management configs.
+ */
+@AutoService(ConfigurationModule.class)
+public class ClusterManagementConfigurationModule implements ConfigurationModule {
+    @Override
+    public ConfigurationType type() {
+        return ConfigurationType.LOCAL;
+    }
+
+    @Override
+    public Collection<RootKey<?, ?>> rootKeys() {
+        return List.of(ClusterManagementConfiguration.KEY);
+    }
+}
diff --git a/modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/configuration/ClusterManagementConfigurationSchema.java b/modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/configuration/ClusterManagementConfigurationSchema.java
new file mode 100644
index 0000000000..ab38f24f27
--- /dev/null
+++ b/modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/configuration/ClusterManagementConfigurationSchema.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cluster.management.configuration;
+
+import java.util.concurrent.TimeUnit;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
+import org.apache.ignite.configuration.annotation.ConfigurationType;
+import org.apache.ignite.configuration.annotation.Value;
+import org.apache.ignite.configuration.validation.Range;
+
+/**
+ * Cluster management configuration schema.
+ */
+@ConfigurationRoot(rootName = "cluster", type = ConfigurationType.LOCAL)
+public class ClusterManagementConfigurationSchema {
+    /** Invoke timeout used by Cluster Management module (ms). */
+    @Value(hasDefault = true)
+    @Range(min = 1)
+    public long networkInvokeTimeout = 500;
+
+    /**
+     * Delay between a moment a node drops out from the physical topology and when it gets removed from the logical topology (ms).
+     */
+    @Value(hasDefault = true)
+    @Range(min = 0)
+    // TODO: IGNITE-18630 - change this to a sensible default.
+    public long failoverTimeout = 0;
+
+    /** Maximum amount of time a validated node that has not yet completed the join is allowed to remain validated (ms). */
+    @Value(hasDefault = true)
+    @Range(min = 1)
+    public long incompleteJoinTimeout = TimeUnit.HOURS.toMillis(1);
+}
diff --git a/modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/raft/CmgRaftGroupListener.java b/modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/raft/CmgRaftGroupListener.java
index f58f332f85..ce53271dc1 100644
--- a/modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/raft/CmgRaftGroupListener.java
+++ b/modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/raft/CmgRaftGroupListener.java
@@ -27,6 +27,7 @@ import java.util.function.Consumer;
 import java.util.function.LongConsumer;
 import java.util.stream.Collectors;
 import org.apache.ignite.internal.cluster.management.ClusterState;
+import org.apache.ignite.internal.cluster.management.configuration.ClusterManagementConfiguration;
 import org.apache.ignite.internal.cluster.management.raft.commands.ClusterNodeMessage;
 import org.apache.ignite.internal.cluster.management.raft.commands.InitCmgStateCommand;
 import org.apache.ignite.internal.cluster.management.raft.commands.JoinReadyCommand;
@@ -67,13 +68,19 @@ public class CmgRaftGroupListener implements RaftGroupListener {
      *
      * @param storage Storage where this listener local data will be stored.
      * @param logicalTopology Logical topology that will be updated by this listener.
+     * @param configuration Cluster management configuration.
      * @param onLogicalTopologyChanged Callback invoked (with the corresponding RAFT term) when logical topology gets changed.
      */
-    public CmgRaftGroupListener(ClusterStateStorage storage, LogicalTopology logicalTopology, LongConsumer onLogicalTopologyChanged) {
+    public CmgRaftGroupListener(
+            ClusterStateStorage storage,
+            LogicalTopology logicalTopology,
+            ClusterManagementConfiguration configuration,
+            LongConsumer onLogicalTopologyChanged
+    ) {
         this.storage = new RaftStorageManager(storage);
         this.logicalTopology = logicalTopology;
         this.onLogicalTopologyChanged = onLogicalTopologyChanged;
-        this.validationManager = new ValidationManager(this.storage, this.logicalTopology);
+        this.validationManager = new ValidationManager(this.storage, logicalTopology, configuration);
     }
 
     @Override
diff --git a/modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/raft/ValidationManager.java b/modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/raft/ValidationManager.java
index c01cb7e92a..084e86a05a 100644
--- a/modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/raft/ValidationManager.java
+++ b/modules/cluster-management/src/main/java/org/apache/ignite/internal/cluster/management/raft/ValidationManager.java
@@ -26,6 +26,7 @@ import java.util.concurrent.TimeUnit;
 import org.apache.ignite.internal.close.ManuallyCloseable;
 import org.apache.ignite.internal.cluster.management.ClusterState;
 import org.apache.ignite.internal.cluster.management.ClusterTag;
+import org.apache.ignite.internal.cluster.management.configuration.ClusterManagementConfiguration;
 import org.apache.ignite.internal.cluster.management.raft.commands.InitCmgStateCommand;
 import org.apache.ignite.internal.cluster.management.raft.commands.JoinReadyCommand;
 import org.apache.ignite.internal.cluster.management.raft.responses.ValidationErrorResponse;
@@ -55,14 +56,17 @@ class ValidationManager implements ManuallyCloseable {
 
     private final LogicalTopology logicalTopology;
 
+    private final ClusterManagementConfiguration configuration;
+
     /**
      * Map for storing tasks, submitted to the {@link #executor}, so that it is possible to cancel them.
      */
     private final Map<String, Future<?>> cleanupFutures = new ConcurrentHashMap<>();
 
-    ValidationManager(RaftStorageManager storage, LogicalTopology logicalTopology) {
+    ValidationManager(RaftStorageManager storage, LogicalTopology logicalTopology, ClusterManagementConfiguration configuration) {
         this.storage = storage;
         this.logicalTopology = logicalTopology;
+        this.configuration = configuration;
 
         // Schedule removal of possibly stale node IDs in case the leader has changed or the node has been restarted.
         storage.getValidatedNodeIds().forEach(this::scheduleValidatedNodeRemoval);
@@ -167,14 +171,13 @@ class ValidationManager implements ManuallyCloseable {
     }
 
     private void scheduleValidatedNodeRemoval(String nodeId) {
-        // TODO: delay should be configurable, see https://issues.apache.org/jira/browse/IGNITE-16785
         Future<?> future = executor.schedule(() -> {
             LOG.info("Removing node from the list of validated nodes since no JoinReady requests have been received [node={}]", nodeId);
 
             cleanupFutures.remove(nodeId);
 
             storage.removeValidatedNode(nodeId);
-        }, 1, TimeUnit.HOURS);
+        }, configuration.incompleteJoinTimeout().value(), TimeUnit.MILLISECONDS);
 
         cleanupFutures.put(nodeId, future);
     }
diff --git a/modules/cluster-management/src/test/java/org/apache/ignite/internal/cluster/management/raft/CmgRaftGroupListenerTest.java b/modules/cluster-management/src/test/java/org/apache/ignite/internal/cluster/management/raft/CmgRaftGroupListenerTest.java
index c6ce9b65a9..38b6ec877b 100644
--- a/modules/cluster-management/src/test/java/org/apache/ignite/internal/cluster/management/raft/CmgRaftGroupListenerTest.java
+++ b/modules/cluster-management/src/test/java/org/apache/ignite/internal/cluster/management/raft/CmgRaftGroupListenerTest.java
@@ -40,11 +40,14 @@ import java.util.Set;
 import java.util.function.LongConsumer;
 import org.apache.ignite.internal.cluster.management.ClusterState;
 import org.apache.ignite.internal.cluster.management.ClusterTag;
+import org.apache.ignite.internal.cluster.management.configuration.ClusterManagementConfiguration;
 import org.apache.ignite.internal.cluster.management.network.messages.CmgMessagesFactory;
 import org.apache.ignite.internal.cluster.management.raft.commands.ClusterNodeMessage;
 import org.apache.ignite.internal.cluster.management.raft.commands.JoinRequestCommand;
 import org.apache.ignite.internal.cluster.management.topology.LogicalTopology;
 import org.apache.ignite.internal.cluster.management.topology.LogicalTopologyImpl;
+import org.apache.ignite.internal.configuration.testframework.ConfigurationExtension;
+import org.apache.ignite.internal.configuration.testframework.InjectConfiguration;
 import org.apache.ignite.internal.properties.IgniteProductVersion;
 import org.apache.ignite.internal.raft.Command;
 import org.apache.ignite.internal.raft.service.CommandClosure;
@@ -52,18 +55,23 @@ import org.jetbrains.annotations.Nullable;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
 
 /**
  * Tests for the {@link CmgRaftGroupListener}.
  */
+@ExtendWith(ConfigurationExtension.class)
 public class CmgRaftGroupListenerTest {
+    @InjectConfiguration
+    private ClusterManagementConfiguration clusterManagementConfiguration;
+
     private final ClusterStateStorage storage = spy(new TestClusterStateStorage());
 
     private final LongConsumer onLogicalTopologyChanged = mock(LongConsumer.class);
 
     private final LogicalTopology logicalTopology = spy(new LogicalTopologyImpl(storage));
 
-    private final CmgRaftGroupListener listener = new CmgRaftGroupListener(storage, logicalTopology, onLogicalTopologyChanged);
+    private CmgRaftGroupListener listener;
 
     private final CmgMessagesFactory msgFactory = new CmgMessagesFactory();
 
@@ -82,6 +90,8 @@ public class CmgRaftGroupListenerTest {
     @BeforeEach
     void setUp() {
         storage.start();
+
+        listener = new CmgRaftGroupListener(storage, logicalTopology, clusterManagementConfiguration, onLogicalTopologyChanged);
     }
 
     @AfterEach
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/ItDistributedConfigurationPropertiesTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/ItDistributedConfigurationPropertiesTest.java
index 5197674999..fa44708e99 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/ItDistributedConfigurationPropertiesTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/ItDistributedConfigurationPropertiesTest.java
@@ -38,6 +38,7 @@ import org.apache.ignite.configuration.annotation.ConfigurationRoot;
 import org.apache.ignite.configuration.annotation.ConfigurationType;
 import org.apache.ignite.configuration.annotation.Value;
 import org.apache.ignite.internal.cluster.management.ClusterManagementGroupManager;
+import org.apache.ignite.internal.cluster.management.configuration.ClusterManagementConfiguration;
 import org.apache.ignite.internal.cluster.management.raft.TestClusterStateStorage;
 import org.apache.ignite.internal.cluster.management.topology.LogicalTopologyImpl;
 import org.apache.ignite.internal.configuration.storage.ConfigurationStorageListener;
@@ -83,6 +84,9 @@ public class ItDistributedConfigurationPropertiesTest {
     @InjectConfiguration
     private static RaftConfiguration raftConfiguration;
 
+    @InjectConfiguration
+    private static ClusterManagementConfiguration clusterManagementConfiguration;
+
     /**
      * An emulation of an Ignite node, that only contains components necessary for tests.
      */
@@ -130,7 +134,8 @@ public class ItDistributedConfigurationPropertiesTest {
                     clusterService,
                     raftManager,
                     clusterStateStorage,
-                    logicalTopologyService
+                    logicalTopologyService,
+                    clusterManagementConfiguration
             );
 
             metaStorageManager = new MetaStorageManagerImpl(
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/storage/ItDistributedConfigurationStorageTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/storage/ItDistributedConfigurationStorageTest.java
index 7b31d379aa..791df8a8cb 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/storage/ItDistributedConfigurationStorageTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/storage/ItDistributedConfigurationStorageTest.java
@@ -32,6 +32,7 @@ import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 import java.util.stream.Stream;
 import org.apache.ignite.internal.cluster.management.ClusterManagementGroupManager;
+import org.apache.ignite.internal.cluster.management.configuration.ClusterManagementConfiguration;
 import org.apache.ignite.internal.cluster.management.raft.TestClusterStateStorage;
 import org.apache.ignite.internal.cluster.management.topology.LogicalTopologyImpl;
 import org.apache.ignite.internal.configuration.testframework.ConfigurationExtension;
@@ -64,6 +65,9 @@ public class ItDistributedConfigurationStorageTest {
     @InjectConfiguration
     private static RaftConfiguration raftConfiguration;
 
+    @InjectConfiguration
+    private static ClusterManagementConfiguration clusterManagementConfiguration;
+
     /**
      * An emulation of an Ignite node, that only contains components necessary for tests.
      */
@@ -104,7 +108,8 @@ public class ItDistributedConfigurationStorageTest {
                     clusterService,
                     raftManager,
                     clusterStateStorage,
-                    logicalTopologyService
+                    logicalTopologyService,
+                    clusterManagementConfiguration
             );
 
             metaStorageManager = new MetaStorageManagerImpl(
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/storage/ItRebalanceDistributedTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/storage/ItRebalanceDistributedTest.java
index fed69aedd6..6392a3fea2 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/storage/ItRebalanceDistributedTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/configuration/storage/ItRebalanceDistributedTest.java
@@ -40,6 +40,7 @@ import org.apache.ignite.client.handler.configuration.ClientConnectorConfigurati
 import org.apache.ignite.internal.affinity.Assignment;
 import org.apache.ignite.internal.baseline.BaselineManager;
 import org.apache.ignite.internal.cluster.management.ClusterManagementGroupManager;
+import org.apache.ignite.internal.cluster.management.configuration.ClusterManagementConfiguration;
 import org.apache.ignite.internal.cluster.management.raft.TestClusterStateStorage;
 import org.apache.ignite.internal.cluster.management.topology.LogicalTopologyImpl;
 import org.apache.ignite.internal.configuration.ConfigurationManager;
@@ -129,6 +130,9 @@ public class ItRebalanceDistributedTest {
     @InjectConfiguration
     private static RaftConfiguration raftConfiguration;
 
+    @InjectConfiguration
+    private static ClusterManagementConfiguration clusterManagementConfiguration;
+
     @WorkDirectory
     private Path workDir;
 
@@ -501,7 +505,8 @@ public class ItRebalanceDistributedTest {
                     clusterService,
                     raftManager,
                     clusterStateStorage,
-                    logicalTopologyService
+                    logicalTopologyService,
+                    clusterManagementConfiguration
             );
 
             metaStorageManager = new MetaStorageManagerImpl(
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItIgniteNodeRestartTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItIgniteNodeRestartTest.java
index 0083fe05c5..c6a360b525 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItIgniteNodeRestartTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/ItIgniteNodeRestartTest.java
@@ -56,6 +56,7 @@ import org.apache.ignite.IgnitionManager;
 import org.apache.ignite.internal.app.IgniteImpl;
 import org.apache.ignite.internal.baseline.BaselineManager;
 import org.apache.ignite.internal.cluster.management.ClusterManagementGroupManager;
+import org.apache.ignite.internal.cluster.management.configuration.ClusterManagementConfiguration;
 import org.apache.ignite.internal.cluster.management.raft.RocksDbClusterStateStorage;
 import org.apache.ignite.internal.cluster.management.topology.LogicalTopologyImpl;
 import org.apache.ignite.internal.configuration.ConfigurationManager;
@@ -156,6 +157,9 @@ public class ItIgniteNodeRestartTest extends IgniteAbstractTest {
     @InjectConfiguration
     private static RaftConfiguration raftConfiguration;
 
+    @InjectConfiguration
+    private static ClusterManagementConfiguration clusterManagementConfiguration;
+
     private final List<String> clusterNodesNames = new ArrayList<>();
 
     /** Cluster nodes. */
@@ -267,7 +271,8 @@ public class ItIgniteNodeRestartTest extends IgniteAbstractTest {
                 clusterSvc,
                 raftMgr,
                 clusterStateStorage,
-                logicalTopologyService
+                logicalTopologyService,
+                clusterManagementConfiguration
         );
 
         var metaStorageMgr = new MetaStorageManagerImpl(
diff --git a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
index 1affcc2468..008116e5e0 100644
--- a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
+++ b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
@@ -38,6 +38,7 @@ import org.apache.ignite.client.handler.ClientHandlerModule;
 import org.apache.ignite.compute.IgniteCompute;
 import org.apache.ignite.internal.baseline.BaselineManager;
 import org.apache.ignite.internal.cluster.management.ClusterManagementGroupManager;
+import org.apache.ignite.internal.cluster.management.configuration.ClusterManagementConfiguration;
 import org.apache.ignite.internal.cluster.management.raft.ClusterStateStorage;
 import org.apache.ignite.internal.cluster.management.raft.RocksDbClusterStateStorage;
 import org.apache.ignite.internal.cluster.management.rest.ClusterManagementRestFactory;
@@ -281,7 +282,9 @@ public class IgniteImpl implements Ignite {
                 modules.local().polymorphicSchemaExtensions()
         );
 
-        NetworkConfiguration networkConfiguration = nodeCfgMgr.configurationRegistry().getConfiguration(NetworkConfiguration.KEY);
+        ConfigurationRegistry nodeConfigRegistry = nodeCfgMgr.configurationRegistry();
+
+        NetworkConfiguration networkConfiguration = nodeConfigRegistry.getConfiguration(NetworkConfiguration.KEY);
 
         MessageSerializationRegistry serializationRegistry = createSerializationRegistry(serviceProviderClassLoader);
 
@@ -298,14 +301,14 @@ public class IgniteImpl implements Ignite {
         computeComponent = new ComputeComponentImpl(
                 this,
                 clusterSvc.messagingService(),
-                nodeCfgMgr.configurationRegistry().getConfiguration(ComputeConfiguration.KEY)
+                nodeConfigRegistry.getConfiguration(ComputeConfiguration.KEY)
         );
 
         clock = new HybridClockImpl();
 
         raftMgr = new Loza(
                 clusterSvc,
-                nodeCfgMgr.configurationRegistry().getConfiguration(RaftConfiguration.KEY),
+                nodeConfigRegistry.getConfiguration(RaftConfiguration.KEY),
                 workDir,
                 clock
         );
@@ -332,7 +335,8 @@ public class IgniteImpl implements Ignite {
                 clusterSvc,
                 raftMgr,
                 clusterStateStorage,
-                logicalTopology
+                logicalTopology,
+                nodeConfigRegistry.getConfiguration(ClusterManagementConfiguration.KEY)
         );
 
         logicalTopologyService = new LogicalTopologyServiceImpl(logicalTopology, cmgMgr);
@@ -357,9 +361,11 @@ public class IgniteImpl implements Ignite {
                 modules.distributed().polymorphicSchemaExtensions()
         );
 
-        metricManager.configure(clusterCfgMgr.configurationRegistry().getConfiguration(MetricConfiguration.KEY));
+        ConfigurationRegistry clusterConfigRegistry = clusterCfgMgr.configurationRegistry();
+
+        metricManager.configure(clusterConfigRegistry.getConfiguration(MetricConfiguration.KEY));
 
-        DistributionZonesConfiguration zonesConfiguration = clusterCfgMgr.configurationRegistry()
+        DistributionZonesConfiguration zonesConfiguration = clusterConfigRegistry
                 .getConfiguration(DistributionZonesConfiguration.KEY);
 
         restComponent = createRestComponent(name);
@@ -373,7 +379,7 @@ public class IgniteImpl implements Ignite {
         );
 
         Consumer<Function<Long, CompletableFuture<?>>> registry =
-                c -> clusterCfgMgr.configurationRegistry().listenUpdateStorageRevision(c::apply);
+                c -> clusterConfigRegistry.listenUpdateStorageRevision(c::apply);
 
         DataStorageModules dataStorageModules = new DataStorageModules(
                 ServiceLoader.load(DataStorageModule.class, serviceProviderClassLoader)
@@ -381,13 +387,13 @@ public class IgniteImpl implements Ignite {
 
         Path storagePath = getPartitionsStorePath(workDir);
 
-        TablesConfiguration tablesConfiguration = clusterCfgMgr.configurationRegistry().getConfiguration(TablesConfiguration.KEY);
+        TablesConfiguration tablesConfiguration = clusterConfigRegistry.getConfiguration(TablesConfiguration.KEY);
 
         dataStorageMgr = new DataStorageManager(
                 tablesConfiguration,
                 dataStorageModules.createStorageEngines(
                         name,
-                        clusterCfgMgr.configurationRegistry(),
+                        clusterConfigRegistry,
                         storagePath,
                         longJvmPauseDetector
                 )
@@ -452,7 +458,7 @@ public class IgniteImpl implements Ignite {
                 qryEngine,
                 distributedTblMgr,
                 new IgniteTransactionsImpl(txManager),
-                nodeCfgMgr.configurationRegistry(),
+                nodeConfigRegistry,
                 compute,
                 clusterSvc,
                 nettyBootstrapFactory,