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

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


The following commit(s) were added to refs/heads/master by this push:
     new 3338e95  Improve auto enter maintenance mode (#1650)
3338e95 is described below

commit 3338e95f149b0fe686eabbf0f0063dd03b45c2a6
Author: Huizhi Lu <[email protected]>
AuthorDate: Mon Feb 22 19:38:42 2021 -0800

    Improve auto enter maintenance mode (#1650)
    
    Assume enter M mode threshold is 5. Now 20 nodes are down at the same time, 
the running pipeline creates the maintenance znode. Instead of using the 
maintenance rebalancer immediately, this running pipeline still continues with 
the normal rebalancer, which moves new partitions on the online instances.
    
    This commit improves auto enter maintenance mode logic by enabling 
maintenance mode in the data cache, so the best possible mapping can be 
computed by the maintenance rebalancer immediately for the first pipeline.
---
 .../dataproviders/BaseControllerDataProvider.java  |  9 +++
 .../stages/BestPossibleStateCalcStage.java         | 13 ++--
 .../stages/TestBestPossibleStateCalcStage.java     | 75 +++++++++++++++++++++-
 .../java/org/apache/helix/mock/MockManager.java    |  3 +-
 4 files changed, 92 insertions(+), 8 deletions(-)

diff --git 
a/helix-core/src/main/java/org/apache/helix/controller/dataproviders/BaseControllerDataProvider.java
 
b/helix-core/src/main/java/org/apache/helix/controller/dataproviders/BaseControllerDataProvider.java
index 070c70a..6ce25e2 100644
--- 
a/helix-core/src/main/java/org/apache/helix/controller/dataproviders/BaseControllerDataProvider.java
+++ 
b/helix-core/src/main/java/org/apache/helix/controller/dataproviders/BaseControllerDataProvider.java
@@ -947,6 +947,15 @@ public class BaseControllerDataProvider implements 
ControlContextProvider {
     return _isMaintenanceModeEnabled;
   }
 
+  /**
+   * Mark the pipeline to enable maintenance mode. It is not supposed to use 
anywhere
+   * except in the auto enter maintenance mode logic when offline instances 
exceeded.
+   */
+  // TODO: refactor it to writable cache once read-only/writable caches are 
separated.
+  public void enableMaintenanceMode() {
+    _isMaintenanceModeEnabled = true;
+  }
+
   public boolean hasMaintenanceSignalChanged() {
     return _hasMaintenanceSignalChanged;
   }
diff --git 
a/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateCalcStage.java
 
b/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateCalcStage.java
index 7b346ec..7c1155d 100644
--- 
a/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateCalcStage.java
+++ 
b/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateCalcStage.java
@@ -116,8 +116,8 @@ public class BestPossibleStateCalcStage extends 
AbstractBaseStage {
         event.getAttribute(AttributeName.clusterStatusMonitor.name());
     WagedRebalancer wagedRebalancer = 
event.getAttribute(AttributeName.STATEFUL_REBALANCER.name());
 
-    // Check whether the offline/disabled instance count in the cluster 
reaches the set limit,
-    // if yes, pause the rebalancer.
+    // Check whether the offline/disabled instance count in the cluster 
exceeds the set limit,
+    // if yes, put the cluster into maintenance mode.
     boolean isValid =
         validateOfflineInstancesLimit(cache, 
event.getAttribute(AttributeName.helixmanager.name()));
 
@@ -193,7 +193,7 @@ public class BestPossibleStateCalcStage extends 
AbstractBaseStage {
   }
 
   // Check whether the offline/disabled instance count in the cluster reaches 
the set limit,
-  // if yes, pause the rebalancer, and throw exception to terminate rebalance 
cycle.
+  // if yes, auto enable maintenance mode, and use the maintenance rebalancer 
for this pipeline.
   private boolean validateOfflineInstancesLimit(final 
ResourceControllerDataProvider cache,
       final HelixManager manager) {
     int maxOfflineInstancesAllowed = 
cache.getClusterConfig().getMaxOfflineInstancesAllowed();
@@ -201,7 +201,8 @@ public class BestPossibleStateCalcStage extends 
AbstractBaseStage {
       int offlineCount = cache.getAllInstances().size() - 
cache.getEnabledLiveInstances().size();
       if (offlineCount > maxOfflineInstancesAllowed) {
         String errMsg = String.format(
-            "Offline Instances count %d greater than allowed count %d. Stop 
rebalance and put the cluster %s into maintenance mode.",
+            "Offline Instances count %d greater than allowed count %d. Put 
cluster %s into "
+                + "maintenance mode.",
             offlineCount, maxOfflineInstancesAllowed, cache.getClusterName());
         if (manager != null) {
           if (manager.getHelixDataAccessor()
@@ -215,6 +216,10 @@ public class BestPossibleStateCalcStage extends 
AbstractBaseStage {
           LogUtil.logError(logger, _eventId, "Failed to put cluster " + 
cache.getClusterName()
               + " into maintenance mode, HelixManager is not set!");
         }
+
+        // Enable maintenance mode in cache so the maintenance rebalancer is 
used for this pipeline
+        cache.enableMaintenanceMode();
+
         return false;
       }
     }
diff --git 
a/helix-core/src/test/java/org/apache/helix/controller/stages/TestBestPossibleStateCalcStage.java
 
b/helix-core/src/test/java/org/apache/helix/controller/stages/TestBestPossibleStateCalcStage.java
index ba70606..7289dc3 100644
--- 
a/helix-core/src/test/java/org/apache/helix/controller/stages/TestBestPossibleStateCalcStage.java
+++ 
b/helix-core/src/test/java/org/apache/helix/controller/stages/TestBestPossibleStateCalcStage.java
@@ -20,14 +20,16 @@ package org.apache.helix.controller.stages;
  */
 
 import java.util.Date;
+import java.util.List;
 import java.util.Map;
 
 import 
org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
 import org.apache.helix.model.BuiltInStateModelDefinitions;
+import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.IdealState.RebalanceMode;
 import org.apache.helix.model.Partition;
 import org.apache.helix.model.Resource;
-import org.testng.AssertJUnit;
+import org.testng.Assert;
 import org.testng.annotations.Test;
 
 public class TestBestPossibleStateCalcStage extends BaseStageTest {
@@ -66,10 +68,79 @@ public class TestBestPossibleStateCalcStage extends 
BaseStageTest {
         event.getAttribute(AttributeName.BEST_POSSIBLE_STATE.name());
     for (int p = 0; p < 5; p++) {
       Partition resource = new Partition("testResourceName_" + p);
-      AssertJUnit.assertEquals("MASTER", 
output.getInstanceStateMap("testResourceName", resource)
+      Assert.assertEquals("MASTER", 
output.getInstanceStateMap("testResourceName", resource)
           .get("localhost_" + (p + 1) % 5));
     }
     System.out.println("END TestBestPossibleStateCalcStage at "
         + new Date(System.currentTimeMillis()));
   }
+
+  /*
+   * Tests the pipeline detects offline instances exceed the threshold and 
auto enters maintenance,
+   * the maintenance rebalancer is used immediately. No bootstraps in the best 
possible output.
+   */
+  @Test
+  public void testAutoEnterMaintenanceWhenExceedingOfflineNodes() {
+    String[] resources = new String[]{"testResourceName"};
+    int numInstances = 3;
+    int numPartitions = 3;
+
+    setupIdealState(numInstances, resources, numPartitions, 1, 
RebalanceMode.FULL_AUTO,
+        BuiltInStateModelDefinitions.MasterSlave.name());
+    setupInstances(numInstances);
+    List<String> liveInstances = setupLiveInstances(numInstances);
+    setupStateModel();
+
+    // Set offline instances threshold
+    ClusterConfig clusterConfig = 
accessor.getProperty(accessor.keyBuilder().clusterConfig());
+    clusterConfig.setMaxOfflineInstancesAllowed(1);
+    setClusterConfig(clusterConfig);
+
+    Map<String, Resource> resourceMap =
+        getResourceMap(resources, numPartitions, 
BuiltInStateModelDefinitions.MasterSlave.name());
+    CurrentStateOutput currentStateOutput = new CurrentStateOutput();
+
+    for (int p = 0; p < numPartitions; p++) {
+      Partition partition = new Partition("testResourceName_" + p);
+      currentStateOutput
+          .setCurrentState("testResourceName", partition, "localhost_" + (p + 
1) % numInstances,
+              "MASTER");
+    }
+
+    // Disable 2 instances so the pipeline should enter maintenance
+    for (int i = 0; i < 2; i++) {
+      admin.enableInstance(_clusterName, liveInstances.get(i), false);
+    }
+
+    event.addAttribute(AttributeName.helixmanager.name(), manager);
+    event.addAttribute(AttributeName.RESOURCES.name(), resourceMap);
+    event.addAttribute(AttributeName.RESOURCES_TO_REBALANCE.name(), 
resourceMap);
+    event.addAttribute(AttributeName.CURRENT_STATE.name(), currentStateOutput);
+    event.addAttribute(AttributeName.ControllerDataProvider.name(),
+        new ResourceControllerDataProvider());
+
+    runStage(event, new ReadClusterDataStage());
+    runStage(event, new BestPossibleStateCalcStage());
+
+    BestPossibleStateOutput output = 
event.getAttribute(AttributeName.BEST_POSSIBLE_STATE.name());
+
+    // State on the disabled instances should be OFFLINE instead of DROPPED
+    // because of maintenance rebalancer.
+    Assert.assertEquals(
+        output.getInstanceStateMap("testResourceName", new 
Partition("testResourceName_2"))
+            .get("localhost_0"),
+        "OFFLINE",
+        "Actual state should not be DROPPED");
+
+    Assert.assertEquals(
+        output.getInstanceStateMap("testResourceName", new 
Partition("testResourceName_0"))
+            .get("localhost_1"),
+        "OFFLINE",
+        "Actual state should not be DROPPED");
+
+    // No state change for localhost_2 because the replica is already MASTER
+    Assert.assertNull(
+        output.getInstanceStateMap("testResourceName", new 
Partition("testResourceName_1"))
+            .get("localhost_2"));
+  }
 }
diff --git a/helix-core/src/test/java/org/apache/helix/mock/MockManager.java 
b/helix-core/src/test/java/org/apache/helix/mock/MockManager.java
index cbaa149..afa8d98 100644
--- a/helix-core/src/test/java/org/apache/helix/mock/MockManager.java
+++ b/helix-core/src/test/java/org/apache/helix/mock/MockManager.java
@@ -259,8 +259,7 @@ public class MockManager implements HelixManager {
 
   @Override
   public HelixAdmin getClusterManagmentTool() {
-    // TODO Auto-generated method stub
-    return null;
+    return new MockHelixAdmin(this);
   }
 
   @Override

Reply via email to