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

siddhant pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new 7dcae6715c HDDS-9593. Replication Manager: Do not count unique origin 
nodes as over-replicated (#5592)
7dcae6715c is described below

commit 7dcae6715cc449b5ffd41b31fdda38fa0560dcc3
Author: Stephen O'Donnell <[email protected]>
AuthorDate: Mon Nov 27 11:54:31 2023 +0000

    HDDS-9593. Replication Manager: Do not count unique origin nodes as 
over-replicated (#5592)
---
 .../container/replication/ReplicationManager.java  |  2 +-
 .../health/RatisReplicationCheckHandler.java       | 49 ++++++++++-
 .../health/TestRatisReplicationCheckHandler.java   | 97 +++++++++++++++++++++-
 3 files changed, 141 insertions(+), 7 deletions(-)

diff --git 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/replication/ReplicationManager.java
 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/replication/ReplicationManager.java
index 34b0183ffc..d34d14ad21 100644
--- 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/replication/ReplicationManager.java
+++ 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/replication/ReplicationManager.java
@@ -243,7 +243,7 @@ public class ReplicationManager implements SCMService {
     this.ecMisReplicationCheckHandler =
         new ECMisReplicationCheckHandler(ecContainerPlacement);
     this.ratisReplicationCheckHandler =
-        new RatisReplicationCheckHandler(ratisContainerPlacement);
+        new RatisReplicationCheckHandler(ratisContainerPlacement, this);
     this.nodeManager = nodeManager;
     this.metrics = ReplicationManagerMetrics.create(this);
 
diff --git 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/replication/health/RatisReplicationCheckHandler.java
 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/replication/health/RatisReplicationCheckHandler.java
index a1d54ad1fc..40531cb04f 100644
--- 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/replication/health/RatisReplicationCheckHandler.java
+++ 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/replication/health/RatisReplicationCheckHandler.java
@@ -17,6 +17,7 @@
 package org.apache.hadoop.hdds.scm.container.replication.health;
 
 import org.apache.hadoop.hdds.protocol.DatanodeDetails;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
 import org.apache.hadoop.hdds.scm.ContainerPlacementStatus;
 import org.apache.hadoop.hdds.scm.PlacementPolicy;
 import org.apache.hadoop.hdds.scm.container.ContainerInfo;
@@ -26,6 +27,9 @@ import 
org.apache.hadoop.hdds.scm.container.replication.ContainerCheckRequest;
 import org.apache.hadoop.hdds.scm.container.replication.ContainerHealthResult;
 import org.apache.hadoop.hdds.scm.container.replication.ContainerReplicaOp;
 import 
org.apache.hadoop.hdds.scm.container.replication.RatisContainerReplicaCount;
+import org.apache.hadoop.hdds.scm.container.replication.ReplicationManager;
+import org.apache.hadoop.hdds.scm.container.replication.ReplicationManagerUtil;
+import org.apache.hadoop.hdds.scm.node.states.NodeNotFoundException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -52,9 +56,12 @@ public class RatisReplicationCheckHandler extends 
AbstractCheck {
    * should be replicated.
    */
   private final PlacementPolicy ratisContainerPlacement;
+  private final ReplicationManager replicationManager;
 
-  public RatisReplicationCheckHandler(PlacementPolicy containerPlacement) {
+  public RatisReplicationCheckHandler(PlacementPolicy containerPlacement,
+      ReplicationManager replicationManager) {
     this.ratisContainerPlacement = containerPlacement;
+    this.replicationManager = replicationManager;
   }
 
   @Override
@@ -190,6 +197,12 @@ public class RatisReplicationCheckHandler extends 
AbstractCheck {
       return replicaCount.toUnderHealthResult();
     }
 
+
+    if (replicaCount.isOverReplicated(false)) {
+      // If the container is over replicated without considering UNHEALTHY
+      // then we know for sure it is over replicated, so mark as such.
+      return replicaCount.toOverHealthResult();
+    }
     /*
     When checking for over replication, consider UNHEALTHY replicas. This means
     that other than checking over replication of healthy replicas (such as 4
@@ -200,9 +213,37 @@ public class RatisReplicationCheckHandler extends 
AbstractCheck {
     RatisContainerReplicaCount consideringUnhealthy =
         new RatisContainerReplicaCount(container, replicas, replicaPendingOps,
             minReplicasForMaintenance, true);
-    boolean isOverReplicated = consideringUnhealthy.isOverReplicated(false);
-    if (isOverReplicated) {
-      return consideringUnhealthy.toOverHealthResult();
+
+    if (consideringUnhealthy.isOverReplicated(false)) {
+      if (container.getState() == HddsProtos.LifeCycleState.CLOSED) {
+        return consideringUnhealthy.toOverHealthResult();
+      } else if (container.getState()
+          == HddsProtos.LifeCycleState.QUASI_CLOSED) {
+        // If the container is quasi-closed and over replicated, we may have a
+        // case where the excess replica is an unhealthy one, but it has a
+        // unique origin and therefore should not be deleted. In this case,
+        // we should not mark the container as over replicated.
+        // We ignore pending deletes, as a container is still over replicated
+        // until the pending delete completes.
+        ContainerReplica toDelete = ReplicationManagerUtil
+            .selectUnhealthyReplicaForDelete(container, replicas, 0,
+                (dnd) -> {
+                  try {
+                    return replicationManager.getNodeStatus(dnd);
+                  } catch (NodeNotFoundException e) {
+                    return null;
+                  }
+                });
+        if (toDelete != null) {
+          // There is at least one unhealthy replica that can be deleted, so
+          // return as over replicated.
+          return consideringUnhealthy.toOverHealthResult();
+        } else {
+          // Even though we have at least 4 replicas with some unhealthy, we
+          // can't delete any of them, so the container is not over replicated.
+          return new ContainerHealthResult.HealthyResult(container);
+        }
+      }
     }
 
     int requiredNodes = container.getReplicationConfig().getRequiredNodes();
diff --git 
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/replication/health/TestRatisReplicationCheckHandler.java
 
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/replication/health/TestRatisReplicationCheckHandler.java
index e2de891966..ad3f3fef6d 100644
--- 
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/replication/health/TestRatisReplicationCheckHandler.java
+++ 
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/replication/health/TestRatisReplicationCheckHandler.java
@@ -36,8 +36,11 @@ import 
org.apache.hadoop.hdds.scm.container.replication.ContainerHealthResult.Mi
 import 
org.apache.hadoop.hdds.scm.container.replication.ContainerHealthResult.OverReplicatedHealthResult;
 import 
org.apache.hadoop.hdds.scm.container.replication.ContainerHealthResult.UnderReplicatedHealthResult;
 import org.apache.hadoop.hdds.scm.container.replication.ContainerReplicaOp;
+import org.apache.hadoop.hdds.scm.container.replication.ReplicationManager;
 import org.apache.hadoop.hdds.scm.container.replication.ReplicationQueue;
 import org.apache.hadoop.hdds.scm.container.replication.ReplicationTestUtil;
+import org.apache.hadoop.hdds.scm.node.NodeStatus;
+import org.apache.hadoop.hdds.scm.node.states.NodeNotFoundException;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -50,6 +53,7 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.UUID;
 
 import static 
org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeOperationalState.DECOMMISSIONED;
 import static 
org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeOperationalState.DECOMMISSIONING;
@@ -76,17 +80,23 @@ public class TestRatisReplicationCheckHandler {
   private ReplicationQueue repQueue;
   private ContainerCheckRequest.Builder requestBuilder;
   private ReplicationManagerReport report;
+  private ReplicationManager replicationManager;
   private int maintenanceRedundancy = 2;
 
   @BeforeEach
-  public void setup() throws IOException {
+  public void setup() throws IOException, NodeNotFoundException {
     containerPlacementPolicy = Mockito.mock(PlacementPolicy.class);
     Mockito.when(containerPlacementPolicy.validateContainerPlacement(
         Mockito.any(),
         Mockito.anyInt()
     )).thenAnswer(invocation ->
         new ContainerPlacementStatusDefault(2, 2, 3));
-    healthCheck = new RatisReplicationCheckHandler(containerPlacementPolicy);
+
+    replicationManager = Mockito.mock(ReplicationManager.class);
+    Mockito.when(replicationManager.getNodeStatus(Mockito.any()))
+        .thenReturn(NodeStatus.inServiceHealthy());
+    healthCheck = new RatisReplicationCheckHandler(containerPlacementPolicy,
+        replicationManager);
     repConfig = RatisReplicationConfig.getInstance(THREE);
     repQueue = new ReplicationQueue();
     report = new ReplicationManagerReport();
@@ -892,4 +902,87 @@ public class TestRatisReplicationCheckHandler {
         ReplicationManagerReport.HealthState.UNDER_REPLICATED));
   }
 
+  @Test
+  public void testExcessReplicasButNotOverReplicatedDuetoUniqueOrigins() {
+    final long sequenceID = 20;
+    final ContainerInfo container = ReplicationTestUtil.createContainerInfo(
+        repConfig, 1, HddsProtos.LifeCycleState.QUASI_CLOSED,
+        sequenceID);
+
+    final Set<ContainerReplica> replicas = new HashSet<>(2);
+    replicas.add(createContainerReplica(container.containerID(), 0,
+        IN_SERVICE, State.QUASI_CLOSED, 1, 1,
+        MockDatanodeDetails.randomDatanodeDetails(),
+        MockDatanodeDetails.randomDatanodeDetails().getUuid(),
+        sequenceID - 1));
+    replicas.add(createContainerReplica(container.containerID(), 0,
+        IN_SERVICE, State.QUASI_CLOSED, 1, 1,
+        MockDatanodeDetails.randomDatanodeDetails(),
+        MockDatanodeDetails.randomDatanodeDetails().getUuid(),
+        sequenceID - 1));
+    replicas.add(createContainerReplica(container.containerID(), 0,
+        IN_SERVICE, State.QUASI_CLOSED, 1, 1,
+        MockDatanodeDetails.randomDatanodeDetails(),
+        MockDatanodeDetails.randomDatanodeDetails().getUuid(),
+        sequenceID - 1));
+
+    replicas.add(createContainerReplica(container.containerID(), 0,
+        IN_SERVICE, State.UNHEALTHY, 1, 1,
+        MockDatanodeDetails.randomDatanodeDetails(),
+        MockDatanodeDetails.randomDatanodeDetails().getUuid(),
+        sequenceID));
+
+    requestBuilder.setContainerReplicas(replicas)
+        .setContainerInfo(container);
+
+    assertFalse(healthCheck.handle(requestBuilder.build()));
+    assertEquals(0, repQueue.underReplicatedQueueSize());
+    assertEquals(0, repQueue.overReplicatedQueueSize());
+    assertEquals(0, report.getStat(
+        ReplicationManagerReport.HealthState.UNDER_REPLICATED));
+    assertEquals(0, report.getStat(
+        ReplicationManagerReport.HealthState.OVER_REPLICATED));
+  }
+
+  @Test
+  public void testExcessReplicasAndOverReplicatedDuetoNonUniqueOrigins() {
+    final long sequenceID = 20;
+    final ContainerInfo container = ReplicationTestUtil.createContainerInfo(
+        repConfig, 1, HddsProtos.LifeCycleState.QUASI_CLOSED,
+        sequenceID);
+
+    UUID origin = UUID.randomUUID();
+    final Set<ContainerReplica> replicas = new HashSet<>(2);
+    replicas.add(createContainerReplica(container.containerID(), 0,
+        IN_SERVICE, State.QUASI_CLOSED, 1, 1,
+        MockDatanodeDetails.randomDatanodeDetails(),
+        MockDatanodeDetails.randomDatanodeDetails().getUuid(),
+        sequenceID - 1));
+    replicas.add(createContainerReplica(container.containerID(), 0,
+        IN_SERVICE, State.QUASI_CLOSED, 1, 1,
+        MockDatanodeDetails.randomDatanodeDetails(),
+        MockDatanodeDetails.randomDatanodeDetails().getUuid(),
+        sequenceID - 1));
+    replicas.add(createContainerReplica(container.containerID(), 0,
+        IN_SERVICE, State.QUASI_CLOSED, 1, 1,
+        MockDatanodeDetails.randomDatanodeDetails(), origin,
+        sequenceID - 1));
+
+    replicas.add(createContainerReplica(container.containerID(), 0,
+        IN_SERVICE, State.UNHEALTHY, 1, 1,
+        MockDatanodeDetails.randomDatanodeDetails(), origin,
+        sequenceID - 1));
+
+    requestBuilder.setContainerReplicas(replicas)
+        .setContainerInfo(container);
+
+    assertTrue(healthCheck.handle(requestBuilder.build()));
+    assertEquals(0, repQueue.underReplicatedQueueSize());
+    assertEquals(1, repQueue.overReplicatedQueueSize());
+    assertEquals(0, report.getStat(
+        ReplicationManagerReport.HealthState.UNDER_REPLICATED));
+    assertEquals(1, report.getStat(
+        ReplicationManagerReport.HealthState.OVER_REPLICATED));
+  }
+
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to