This is an automated email from the ASF dual-hosted git repository.
rpuch 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 c40c10ab33 IGNITE-23768 Prohibit migration from repaired cluster to
old one (#4785)
c40c10ab33 is described below
commit c40c10ab33dac4e74eb6c8d8afcaab81b993ad05
Author: Roman Puchkovskiy <[email protected]>
AuthorDate: Mon Nov 25 21:41:17 2024 +0400
IGNITE-23768 Prohibit migration from repaired cluster to old one (#4785)
---
.../system/SystemDisasterRecoveryManagerImpl.java | 36 +++++++++++++++++++++-
.../SystemDisasterRecoveryManagerImplTest.java | 35 +++++++++++++++++++++
2 files changed, 70 insertions(+), 1 deletion(-)
diff --git
a/modules/system-disaster-recovery/src/main/java/org/apache/ignite/internal/disaster/system/SystemDisasterRecoveryManagerImpl.java
b/modules/system-disaster-recovery/src/main/java/org/apache/ignite/internal/disaster/system/SystemDisasterRecoveryManagerImpl.java
index a4f9fcf78c..5b85cfe011 100644
---
a/modules/system-disaster-recovery/src/main/java/org/apache/ignite/internal/disaster/system/SystemDisasterRecoveryManagerImpl.java
+++
b/modules/system-disaster-recovery/src/main/java/org/apache/ignite/internal/disaster/system/SystemDisasterRecoveryManagerImpl.java
@@ -432,8 +432,21 @@ public class SystemDisasterRecoveryManagerImpl implements
SystemDisasterRecovery
@Override
public CompletableFuture<Void> migrate(ClusterState targetClusterState) {
+ try {
+ return doMigrate(targetClusterState);
+ } catch (MigrateException e) {
+ return failedFuture(e);
+ }
+ }
+
+ private CompletableFuture<Void> doMigrate(ClusterState targetClusterState)
{
if (targetClusterState.formerClusterIds() == null) {
- return failedFuture(new MigrateException("Migration can only
happen using cluster state from a node that saw a cluster reset"));
+ throw new MigrateException("Migration can only happen using
cluster state from a node that saw a cluster reset");
+ }
+
+ ClusterState clusterState = ensureClusterStateIsPresent();
+ if (isDescendantOrSame(clusterState, targetClusterState)) {
+ throw new MigrateException("Migration can only happen from old
cluster to new one, not the other way around");
}
Collection<ClusterNode> nodesInTopology = topologyService.allMembers();
@@ -452,6 +465,27 @@ public class SystemDisasterRecoveryManagerImpl implements
SystemDisasterRecovery
}, restartExecutor);
}
+ private static boolean isDescendantOrSame(ClusterState
potencialDescendant, ClusterState potentialAncestor) {
+ List<UUID> descendantLineage =
extractClusterIdsLineage(potencialDescendant);
+ List<UUID> ancestorLineage =
extractClusterIdsLineage(potentialAncestor);
+
+ return descendantLineage.size() >= ancestorLineage.size()
+ && descendantLineage.subList(0,
ancestorLineage.size()).equals(ancestorLineage);
+ }
+
+ private static List<UUID> extractClusterIdsLineage(ClusterState state) {
+ List<UUID> lineage = new ArrayList<>();
+
+ List<UUID> formerClusterIds = state.formerClusterIds();
+ if (formerClusterIds != null) {
+ lineage.addAll(formerClusterIds);
+ }
+
+ lineage.add(state.clusterTag().clusterId());
+
+ return lineage;
+ }
+
private ResetClusterMessage
buildResetClusterMessageForMigrate(ClusterState clusterState) {
List<UUID> formerClusterIds = clusterState.formerClusterIds();
assert formerClusterIds != null : "formerClusterIds is null, but it
must never be here as it's from a node that saw a CMG reset; "
diff --git
a/modules/system-disaster-recovery/src/test/java/org/apache/ignite/internal/disaster/system/SystemDisasterRecoveryManagerImplTest.java
b/modules/system-disaster-recovery/src/test/java/org/apache/ignite/internal/disaster/system/SystemDisasterRecoveryManagerImplTest.java
index 9f0abc84cf..daf2a8bd1c 100644
---
a/modules/system-disaster-recovery/src/test/java/org/apache/ignite/internal/disaster/system/SystemDisasterRecoveryManagerImplTest.java
+++
b/modules/system-disaster-recovery/src/test/java/org/apache/ignite/internal/disaster/system/SystemDisasterRecoveryManagerImplTest.java
@@ -22,6 +22,7 @@ import static
java.util.concurrent.CompletableFuture.completedFuture;
import static java.util.concurrent.CompletableFuture.failedFuture;
import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.stream.Collectors.toSet;
+import static
org.apache.ignite.internal.cluster.management.ClusterTag.clusterTag;
import static
org.apache.ignite.internal.cluster.management.ClusterTag.randomClusterTag;
import static
org.apache.ignite.internal.testframework.IgniteTestUtils.waitForCondition;
import static
org.apache.ignite.internal.testframework.asserts.CompletableFutureAssert.assertWillThrow;
@@ -589,6 +590,8 @@ class SystemDisasterRecoveryManagerImplTest extends
BaseIgniteAbstractTest {
@Test
void migrateSendsMessagesToAllNodes() {
+ putClusterState();
+
ClusterState newState = newClusterState();
ArgumentCaptor<ResetClusterMessage> messageCaptor =
ArgumentCaptor.forClass(ResetClusterMessage.class);
@@ -639,6 +642,8 @@ class SystemDisasterRecoveryManagerImplTest extends
BaseIgniteAbstractTest {
@Test
void migrateInitiatesRestart() {
+ putClusterState();
+
ClusterState newState = newClusterState();
when(topologyService.allMembers()).thenReturn(List.of(thisNode, node2,
node3));
@@ -650,6 +655,36 @@ class SystemDisasterRecoveryManagerImplTest extends
BaseIgniteAbstractTest {
verify(restarter).initiateRestart();
}
+ @Test
+ void migrationInWrongDirectionIsProhibited() {
+ UUID clusterId1 = randomUUID();
+ UUID clusterId2 = randomUUID();
+ UUID clusterId3 = randomUUID();
+
+ ClusterState oldClusterState = cmgMessagesFactory.clusterState()
+ .cmgNodes(Set.of(thisNodeName))
+ .metaStorageNodes(Set.of(thisNodeName))
+ .version(IgniteProductVersion.CURRENT_VERSION.toString())
+ .clusterTag(clusterTag(cmgMessagesFactory, CLUSTER_NAME,
clusterId2))
+ .initialClusterConfiguration(INITIAL_CONFIGURATION)
+ .formerClusterIds(List.of(clusterId1))
+ .build();
+
+ ClusterState newClusterState = cmgMessagesFactory.clusterState()
+ .cmgNodes(Set.of(thisNodeName))
+ .metaStorageNodes(Set.of(thisNodeName))
+ .version(IgniteProductVersion.CURRENT_VERSION.toString())
+ .clusterTag(clusterTag(cmgMessagesFactory, CLUSTER_NAME,
clusterId3))
+ .initialClusterConfiguration(INITIAL_CONFIGURATION)
+ .formerClusterIds(List.of(clusterId1, clusterId2))
+ .build();
+
+ manager.saveClusterState(newClusterState);
+
+ MigrateException ex =
assertWillThrow(manager.migrate(oldClusterState), MigrateException.class);
+ assertThat(ex.getMessage(), is("Migration can only happen from old
cluster to new one, not the other way around"));
+ }
+
@ParameterizedTest
@ValueSource(ints = {0, -1})
void resetClusterWithMgRequiresPositiveMgReplicationFactor(int
metastorageReplicationFactor) {