You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@helix.apache.org by jx...@apache.org on 2017/05/23 21:28:48 UTC

[2/2] helix git commit: Allow user to enable persisting preference list and best possible state map into IdealState in full-auto mode.

Allow user to enable persisting preference list and best possible state map into IdealState in full-auto mode.


Project: http://git-wip-us.apache.org/repos/asf/helix/repo
Commit: http://git-wip-us.apache.org/repos/asf/helix/commit/8ba068e7
Tree: http://git-wip-us.apache.org/repos/asf/helix/tree/8ba068e7
Diff: http://git-wip-us.apache.org/repos/asf/helix/diff/8ba068e7

Branch: refs/heads/helix-0.6.x
Commit: 8ba068e7b78aedf4743f2da57670384534d1d4f8
Parents: 7c92bf5
Author: Lei Xia <lx...@linkedin.com>
Authored: Tue May 23 13:58:24 2017 -0700
Committer: Lei Xia <lx...@linkedin.com>
Committed: Tue May 23 13:58:24 2017 -0700

----------------------------------------------------------------------
 .../stages/BestPossibleStateCalcStage.java      |   3 +
 .../stages/BestPossibleStateOutput.java         |  42 +++++
 .../stages/PersistAssignmentStage.java          | 172 ++++++++++---------
 .../java/org/apache/helix/model/IdealState.java |  65 +++++--
 .../TestRebalancerPersistAssignments.java       | 126 +++++---------
 5 files changed, 227 insertions(+), 181 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/helix/blob/8ba068e7/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateCalcStage.java
----------------------------------------------------------------------
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 cba0659..526f532 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
@@ -127,6 +127,7 @@ public class BestPossibleStateCalcStage extends AbstractBaseStage {
           rebalancer.init(manager);
           idealState =
               rebalancer.computeNewIdealState(resourceName, idealState, currentStateOutput, cache);
+          output.setPreferenceLists(resourceName, idealState.getPreferenceLists());
 
           // Use the internal MappingCalculator interface to compute the final assignment
           // The next release will support rebalancers that compute the mapping from start to finish
@@ -180,6 +181,8 @@ public class BestPossibleStateCalcStage extends AbstractBaseStage {
       rebalancer = customizedRebalancer;
       break;
     default:
+      logger.error(
+          "Fail to find the rebalancer, invalid rebalance mode " + idealState.getRebalanceMode());
       break;
     }
 

http://git-wip-us.apache.org/repos/asf/helix/blob/8ba068e7/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateOutput.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateOutput.java b/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateOutput.java
index 168a3b0..a3ad56d 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateOutput.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateOutput.java
@@ -21,6 +21,7 @@ package org.apache.helix.controller.stages;
 
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -29,6 +30,8 @@ import org.apache.helix.model.Partition;
 public class BestPossibleStateOutput {
   // Map of resource->partition->instance->state
   Map<String, Map<Partition, Map<String, String>>> _stateMap;
+  /* resource -> partition -> preference list */
+  private Map<String, Map<String, List<String>>> _preferenceLists;
 
   public BestPossibleStateOutput() {
     _stateMap = new HashMap<String, Map<Partition, Map<String, String>>>();
@@ -77,6 +80,45 @@ public class BestPossibleStateOutput {
     return _stateMap;
   }
 
+  public Map<String, Map<String, List<String>>> getPreferenceLists() {
+    return _preferenceLists;
+  }
+
+  public Map<String, List<String>> getPreferenceLists(String resource) {
+    if (_preferenceLists != null && _preferenceLists.containsKey(resource)) {
+      return _preferenceLists.get(resource);
+    }
+
+    return null;
+  }
+
+  public List<String> getPreferenceList(String resource, String partition) {
+    if (_preferenceLists != null && _preferenceLists.containsKey(resource) && _preferenceLists
+        .get(resource).containsKey(partition)) {
+      return _preferenceLists.get(resource).get(partition);
+    }
+
+    return null;
+  }
+
+  public void setPreferenceList(String resource, String partition, List<String> list) {
+    if (_preferenceLists == null) {
+      _preferenceLists = new HashMap<String, Map<String, List<String>>>();
+    }
+    if (!_preferenceLists.containsKey(resource)) {
+      _preferenceLists.put(resource, new HashMap<String, List<String>>());
+    }
+    _preferenceLists.get(resource).put(partition, list);
+  }
+
+  public void setPreferenceLists(String resource,
+      Map<String, List<String>> resourcePreferenceLists) {
+    if (_preferenceLists == null) {
+      _preferenceLists = new HashMap<String, Map<String, List<String>>>();
+    }
+    _preferenceLists.put(resource, resourcePreferenceLists);
+  }
+
   @Override
   public String toString() {
     return _stateMap.toString();

http://git-wip-us.apache.org/repos/asf/helix/blob/8ba068e7/helix-core/src/main/java/org/apache/helix/controller/stages/PersistAssignmentStage.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/controller/stages/PersistAssignmentStage.java b/helix-core/src/main/java/org/apache/helix/controller/stages/PersistAssignmentStage.java
index 9c297f8..425b38b 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/stages/PersistAssignmentStage.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/stages/PersistAssignmentStage.java
@@ -19,19 +19,16 @@ package org.apache.helix.controller.stages;
  * under the License.
  */
 
-import java.util.Collections;
-import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-
+import java.util.Set;
 import org.apache.helix.HelixDataAccessor;
 import org.apache.helix.HelixManager;
 import org.apache.helix.PropertyKey;
 import org.apache.helix.controller.pipeline.AbstractBaseStage;
-import org.apache.helix.model.BuiltInStateModelDefinitions;
 import org.apache.helix.model.ClusterConfig;
 import org.apache.helix.model.IdealState;
-import org.apache.helix.model.MasterSlaveSMD;
 import org.apache.helix.model.Partition;
 import org.apache.helix.model.Resource;
 import org.apache.log4j.Logger;
@@ -49,56 +46,58 @@ public class PersistAssignmentStage extends AbstractBaseStage {
     ClusterDataCache cache = event.getAttribute("ClusterDataCache");
     ClusterConfig clusterConfig = cache.getClusterConfig();
 
-    if (clusterConfig.isPersistBestPossibleAssignment()) {
-      HelixManager helixManager = event.getAttribute("helixmanager");
-      HelixDataAccessor accessor = helixManager.getHelixDataAccessor();
-      PropertyKey.Builder keyBuilder = accessor.keyBuilder();
-      BestPossibleStateOutput bestPossibleAssignments =
-          event.getAttribute(AttributeName.BEST_POSSIBLE_STATE.toString());
-      Map<String, Resource> resourceMap = event.getAttribute(AttributeName.RESOURCES.toString());
-
-      for (String resourceId : bestPossibleAssignments.resourceSet()) {
-        Resource resource = resourceMap.get(resourceId);
-        if (resource != null) {
-          boolean changed = false;
-          Map<Partition, Map<String, String>> bestPossibleAssignment =
-              bestPossibleAssignments.getResourceMap(resourceId);
-          IdealState idealState = cache.getIdealState(resourceId);
-          if (idealState == null) {
-            LOG.warn("IdealState not found for resource " + resourceId);
-            continue;
-          }
-          IdealState.RebalanceMode mode = idealState.getRebalanceMode();
-          if (!mode.equals(IdealState.RebalanceMode.SEMI_AUTO) && !mode
-              .equals(IdealState.RebalanceMode.FULL_AUTO)) {
-            // do not persist assignment for resource in neither semi or full auto.
-            continue;
-          }
+    if (!clusterConfig.isPersistBestPossibleAssignment()) {
+      return;
+    }
 
-          //TODO: temporary solution for Espresso/Dbus backcompatible, should remove this.
-          Map<Partition, Map<String, String>> assignmentToPersist =
-              convertAssignmentPersisted(resource, idealState, bestPossibleAssignment);
-
-          for (Partition partition : resource.getPartitions()) {
-            Map<String, String> instanceMap = assignmentToPersist.get(partition);
-            Map<String, String> existInstanceMap =
-                idealState.getInstanceStateMap(partition.getPartitionName());
-            if (instanceMap == null && existInstanceMap == null) {
-              continue;
-            }
-            if (instanceMap == null || existInstanceMap == null || !instanceMap
-                .equals(existInstanceMap)) {
-              changed = true;
-              break;
-            }
+    BestPossibleStateOutput bestPossibleAssignment =
+        event.getAttribute(AttributeName.BEST_POSSIBLE_STATE.name());
+
+    HelixManager helixManager = event.getAttribute("helixmanager");
+    HelixDataAccessor accessor = helixManager.getHelixDataAccessor();
+    PropertyKey.Builder keyBuilder = accessor.keyBuilder();
+    Map<String, Resource> resourceMap = event.getAttribute(AttributeName.RESOURCES.name());
+
+    for (String resourceId : bestPossibleAssignment.resourceSet()) {
+      Resource resource = resourceMap.get(resourceId);
+      if (resource != null) {
+        final IdealState idealState = cache.getIdealState(resourceId);
+        if (idealState == null) {
+          LOG.warn("IdealState not found for resource " + resourceId);
+          continue;
+        }
+        IdealState.RebalanceMode mode = idealState.getRebalanceMode();
+        if (!mode.equals(IdealState.RebalanceMode.SEMI_AUTO) && !mode
+            .equals(IdealState.RebalanceMode.FULL_AUTO)) {
+          // do not persist assignment for resource in neither semi or full auto.
+          continue;
+        }
+
+        boolean needPersist = false;
+        if (mode.equals(IdealState.RebalanceMode.FULL_AUTO)) {
+          // persist preference list in ful-auto mode.
+          Map<String, List<String>> newLists =
+              bestPossibleAssignment.getPreferenceLists(resourceId);
+          if (newLists != null && hasPreferenceListChanged(newLists, idealState)) {
+            idealState.setPreferenceLists(newLists);
+            needPersist = true;
           }
-          if (changed) {
-            for (Partition partition : assignmentToPersist.keySet()) {
-              Map<String, String> instanceMap = assignmentToPersist.get(partition);
-              idealState.setInstanceStateMap(partition.getPartitionName(), instanceMap);
-            }
-            accessor.setProperty(keyBuilder.idealStates(resourceId), idealState);
+        }
+
+        Map<Partition, Map<String, String>> bestPossibleAssignements =
+            bestPossibleAssignment.getResourceMap(resourceId);
+
+        if (bestPossibleAssignements != null && hasInstanceMapChanged(bestPossibleAssignements,
+            idealState)) {
+          for (Partition partition : bestPossibleAssignements.keySet()) {
+            Map<String, String> instanceMap = bestPossibleAssignements.get(partition);
+            idealState.setInstanceStateMap(partition.getPartitionName(), instanceMap);
           }
+          needPersist = true;
+        }
+
+        if (needPersist) {
+          accessor.setProperty(keyBuilder.idealStates(resourceId), idealState);
         }
       }
     }
@@ -108,47 +107,50 @@ public class PersistAssignmentStage extends AbstractBaseStage {
   }
 
   /**
-   * TODO: This is a temporary hacky for back-compatible support of Espresso and Databus,
-   * we should get rid of this conversion as soon as possible.
-   * --- Lei, 2016/9/9.
+   * has the preference list changed from the one persisted in current IdealState
    */
-  private Map<Partition, Map<String, String>> convertAssignmentPersisted(Resource resource,
-      IdealState idealState, Map<Partition, Map<String, String>> bestPossibleAssignment) {
-    String stateModelDef = idealState.getStateModelDefRef();
-    /** Only convert for MasterSlave resources */
-    if (!stateModelDef.equals(BuiltInStateModelDefinitions.MasterSlave.name())) {
-      return bestPossibleAssignment;
+  private boolean hasPreferenceListChanged(Map<String, List<String>> newLists,
+      IdealState idealState) {
+    Map<String, List<String>> existLists = idealState.getPreferenceLists();
+
+    Set<String> partitions = new HashSet<String>(newLists.keySet());
+    partitions.addAll(existLists.keySet());
+
+    for (String partition : partitions) {
+      List<String> assignedInstances = newLists.get(partition);
+      List<String> existingInstances = existLists.get(partition);
+      if (assignedInstances == null && existingInstances == null) {
+        continue;
+      }
+      if (assignedInstances == null || existingInstances == null || !assignedInstances
+          .equals(existingInstances)) {
+        return true;
+      }
     }
 
-    Map<Partition, Map<String, String>> assignmentToPersist =
-        new HashMap<Partition, Map<String, String>>();
-
-    for (Partition partition : resource.getPartitions()) {
-      Map<String, String> instanceMap = new HashMap<String, String>();
-      instanceMap.putAll(bestPossibleAssignment.get(partition));
+    return false;
+  }
 
-      List<String> preferenceList = idealState.getPreferenceList(partition.getPartitionName());
-      boolean hasMaster = false;
-      for (String ins : preferenceList) {
-        String state = instanceMap.get(ins);
-        if (state == null || (!state.equals(MasterSlaveSMD.States.SLAVE.name()) && !state
-            .equals(MasterSlaveSMD.States.MASTER.name()))) {
-          instanceMap.put(ins, MasterSlaveSMD.States.SLAVE.name());
-        }
+  private boolean hasInstanceMapChanged(Map<Partition, Map<String, String>> newAssiments,
+      IdealState idealState) {
+    Set<Partition> partitions = new HashSet<Partition>(newAssiments.keySet());
+    for (String p : idealState.getPartitionSet()) {
+      partitions.add(new Partition(p));
+    }
 
-        if (state != null && state.equals(MasterSlaveSMD.States.MASTER.name())) {
-          hasMaster = true;
-        }
+    for (Partition partition : partitions) {
+      Map<String, String> instanceMap = newAssiments.get(partition);
+      Map<String, String> existInstanceMap =
+          idealState.getInstanceStateMap(partition.getPartitionName());
+      if (instanceMap == null && existInstanceMap == null) {
+        continue;
       }
-
-      // if no master, just pick the first node in the preference list as the master.
-      if (!hasMaster && preferenceList.size() > 0) {
-        instanceMap.put(preferenceList.get(0), MasterSlaveSMD.States.MASTER.name());
+      if (instanceMap == null || existInstanceMap == null || !instanceMap
+          .equals(existInstanceMap)) {
+        return true;
       }
-
-      assignmentToPersist.put(partition, instanceMap);
     }
 
-    return assignmentToPersist;
+    return false;
   }
 }

http://git-wip-us.apache.org/repos/asf/helix/blob/8ba068e7/helix-core/src/main/java/org/apache/helix/model/IdealState.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/model/IdealState.java b/helix-core/src/main/java/org/apache/helix/model/IdealState.java
index 907bd27..48e43d6 100644
--- a/helix-core/src/main/java/org/apache/helix/model/IdealState.java
+++ b/helix-core/src/main/java/org/apache/helix/model/IdealState.java
@@ -352,6 +352,9 @@ public class IdealState extends HelixProperty {
 
   /**
    * Get the current mapping of a partition
+   * CAUTION: In FULL-AUTO mode, this method could return empty map if
+   * {@link ClusterConfig#setPersistBestPossibleAssignment(Boolean)} is set to true.
+   *
    * @param partitionName the name of the partition
    * @return the instances where the replicas live and the state of each
    */
@@ -371,37 +374,75 @@ public class IdealState extends HelixProperty {
   }
 
   /**
-   * Get the instances who host replicas of a partition
+   * Get the instances who host replicas of a partition.
+   * CAUTION: In FULL-AUTO mode, this method could return empty map if
+   * {@link ClusterConfig#setPersistBestPossibleAssignment(Boolean)} is set to true.
+   +
    * @param partitionName the partition to look up
    * @return set of instance names
    */
   public Set<String> getInstanceSet(String partitionName) {
-    if (getRebalanceMode() == RebalanceMode.SEMI_AUTO
-        || getRebalanceMode() == RebalanceMode.FULL_AUTO
-        || getRebalanceMode() == RebalanceMode.USER_DEFINED
-        || getRebalanceMode() == RebalanceMode.TASK) {
+    switch (getRebalanceMode()) {
+    case FULL_AUTO:
+    case SEMI_AUTO:
+    case USER_DEFINED:
+    case TASK:
       List<String> prefList = _record.getListField(partitionName);
-      if (prefList != null) {
+      if (prefList != null && !prefList.isEmpty()) {
         return new TreeSet<String>(prefList);
       } else {
-        logger.warn(partitionName + " does NOT exist");
-        return Collections.emptySet();
+        Map<String, String> stateMap = _record.getMapField(partitionName);
+        if (stateMap != null && !stateMap.isEmpty()) {
+          return new TreeSet<String>(stateMap.keySet());
+        } else {
+          logger.warn(partitionName + " does NOT exist");
+        }
       }
-    } else if (getRebalanceMode() == RebalanceMode.CUSTOMIZED) {
+      break;
+    case CUSTOMIZED:
       Map<String, String> stateMap = _record.getMapField(partitionName);
       if (stateMap != null) {
         return new TreeSet<String>(stateMap.keySet());
       } else {
         logger.warn(partitionName + " does NOT exist");
-        return Collections.emptySet();
       }
-    } else {
+      break;
+    case NONE:
+    default:
       logger.error("Invalid ideal state mode: " + getResourceName());
-      return Collections.emptySet();
+      break;
     }
 
+    return Collections.emptySet();
+  }
+
+  /** Set the preference list of a partition
+   * @param partitionName the name of the partition
+   * @param instanceList the instance preference list
+   */
+  public void setPreferenceList(String partitionName, List<String> instanceList) {
+    _record.setListField(partitionName, instanceList);
+  }
+
+  /**
+   * Set the preference lists for all partitions in this resource.
+   *
+   * @param instanceLists the map of instance preference lists.
+   */
+  public void setPreferenceLists(Map<String, List<String>> instanceLists) {
+    _record.setListFields(instanceLists);
+  }
+
+  /**
+   * Get the preference lists for all partitions
+   *
+   * @return map of lists of instances for all partitions in this resource.
+   */
+  public Map<String, List<String>> getPreferenceLists() {
+    return _record.getListFields();
   }
 
+
   /**
    * Get the preference list of a partition
    * @param partitionName the name of the partition

http://git-wip-us.apache.org/repos/asf/helix/blob/8ba068e7/helix-core/src/test/java/org/apache/helix/integration/TestRebalancerPersistAssignments.java
----------------------------------------------------------------------
diff --git a/helix-core/src/test/java/org/apache/helix/integration/TestRebalancerPersistAssignments.java b/helix-core/src/test/java/org/apache/helix/integration/TestRebalancerPersistAssignments.java
index 3aec847..2a9dc69 100644
--- a/helix-core/src/test/java/org/apache/helix/integration/TestRebalancerPersistAssignments.java
+++ b/helix-core/src/test/java/org/apache/helix/integration/TestRebalancerPersistAssignments.java
@@ -24,11 +24,8 @@ import org.apache.helix.integration.manager.MockParticipantManager;
 import org.apache.helix.model.BuiltInStateModelDefinitions;
 import org.apache.helix.model.IdealState;
 import org.apache.helix.model.IdealState.RebalanceMode;
-import org.apache.helix.model.MasterSlaveSMD;
 import org.apache.helix.tools.ClusterSetup;
 import org.apache.helix.tools.ClusterVerifiers.BestPossibleExternalViewVerifier;
-import org.apache.helix.tools.ClusterVerifiers.ClusterStateVerifier;
-import org.apache.helix.tools.ClusterVerifiers.HelixClusterVerifier;
 import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
@@ -42,6 +39,8 @@ import java.util.Map;
 import java.util.Set;
 
 public class TestRebalancerPersistAssignments extends ZkStandAloneCMTestBase {
+  Set<String> _instanceNames = new HashSet<String>();
+
   @Override
   @BeforeClass
   public void beforeClass() throws Exception {
@@ -69,13 +68,14 @@ public class TestRebalancerPersistAssignments extends ZkStandAloneCMTestBase {
     // start dummy participants
     for (int i = 0; i < NODE_NR; i++) {
       String instanceName = PARTICIPANT_PREFIX + "_" + (START_PORT + i);
+      _instanceNames.add(instanceName);
       _participants[i] = new MockParticipantManager(ZK_ADDR, CLUSTER_NAME, instanceName);
       _participants[i].syncStart();
     }
   }
 
   @DataProvider(name = "rebalanceModes")
-  public static RebalanceMode [][] rebalanceModes() {
+  public static Object [][] rebalanceModes() {
     return new RebalanceMode[][] { {RebalanceMode.SEMI_AUTO}, {RebalanceMode.FULL_AUTO}};
   }
 
@@ -88,23 +88,25 @@ public class TestRebalancerPersistAssignments extends ZkStandAloneCMTestBase {
         BuiltInStateModelDefinitions.LeaderStandby.name(), rebalanceMode.name());
     _setupTool.rebalanceStorageCluster(CLUSTER_NAME, testDb, 3);
 
-    HelixClusterVerifier verifier =
+    BestPossibleExternalViewVerifier.Builder verifierBuilder =
         new BestPossibleExternalViewVerifier.Builder(CLUSTER_NAME).setZkAddr(ZK_ADDR)
-            .setResources(new HashSet<String>(Collections.singleton(testDb))).build();
-    Thread.sleep(500);
-    Assert.assertTrue(verifier.verify());
+            .setResources(new HashSet<String>(Collections.singleton(testDb)));
+
+    Assert.assertTrue(verifierBuilder.build().verify());
 
     // kill 1 node
     _participants[0].syncStop();
 
-    Assert.assertTrue(verifier.verify());
+    Set<String> liveInstances = new HashSet<String>(_instanceNames);
+    liveInstances.remove(_participants[0].getInstanceName());
+    verifierBuilder.setExpectLiveInstances(liveInstances);
+    Assert.assertTrue(verifierBuilder.build().verify());
 
     IdealState idealState =
         _setupTool.getClusterManagementTool().getResourceIdealState(CLUSTER_NAME, testDb);
 
     Set<String> excludedInstances = new HashSet<String>();
     excludedInstances.add(_participants[0].getInstanceName());
-    Thread.sleep(2000);
     verifyAssignmentInIdealStateWithPersistDisabled(idealState, excludedInstances);
 
     // clean up
@@ -124,10 +126,11 @@ public class TestRebalancerPersistAssignments extends ZkStandAloneCMTestBase {
         BuiltInStateModelDefinitions.LeaderStandby.name(), rebalanceMode.name());
     _setupTool.rebalanceStorageCluster(CLUSTER_NAME, testDb, 3);
 
-    HelixClusterVerifier verifier =
+    BestPossibleExternalViewVerifier.Builder verifierBuilder =
         new BestPossibleExternalViewVerifier.Builder(CLUSTER_NAME).setZkAddr(ZK_ADDR)
-            .setResources(new HashSet<String>(Collections.singleton(testDb))).build();
-    Assert.assertTrue(verifier.verify());
+            .setResources(new HashSet<String>(Collections.singleton(testDb)));
+
+    Assert.assertTrue(verifierBuilder.build().verify());
 
     IdealState idealState =
         _setupTool.getClusterManagementTool().getResourceIdealState(CLUSTER_NAME, testDb);
@@ -136,9 +139,10 @@ public class TestRebalancerPersistAssignments extends ZkStandAloneCMTestBase {
     // kill 1 node
     _participants[0].syncStop();
 
-    Boolean result = ClusterStateVerifier.verifyByZkCallback(
-        new ClusterStateVerifier.BestPossAndExtViewZkVerifier(ZK_ADDR, CLUSTER_NAME));
-    Assert.assertTrue(result);
+    Set<String> liveInstances = new HashSet<String>(_instanceNames);
+    liveInstances.remove(_participants[0].getInstanceName());
+    verifierBuilder.setExpectLiveInstances(liveInstances);
+    Assert.assertTrue(verifierBuilder.build().verify());
 
     idealState = _setupTool.getClusterManagementTool().getResourceIdealState(CLUSTER_NAME, testDb);
     // verify that IdealState contains updated assignment in it map fields.
@@ -154,72 +158,8 @@ public class TestRebalancerPersistAssignments extends ZkStandAloneCMTestBase {
     _participants[0].syncStart();
   }
 
-  /**
-   * This test is to test the temporary solution for solving Espresso/Databus back-compatible map format issue.
-   *
-   * @throws Exception
-   */
-  @Test(dependsOnMethods = { "testDisablePersist" })
-  public void testSemiAutoEnablePersistMasterSlave() throws Exception {
-    String testDb = "TestDB1-MasterSlave";
-    enablePersistBestPossibleAssignment(_gZkClient, CLUSTER_NAME, true);
-
-    _setupTool.addResourceToCluster(CLUSTER_NAME, testDb, 5, BuiltInStateModelDefinitions.MasterSlave.name(),
-        RebalanceMode.SEMI_AUTO.name());
-    _setupTool.rebalanceStorageCluster(CLUSTER_NAME, testDb, 3);
-
-    HelixClusterVerifier verifier =
-        new BestPossibleExternalViewVerifier.Builder(CLUSTER_NAME).setZkAddr(ZK_ADDR)
-            .setResources(new HashSet<String>(Collections.singleton(testDb))).build();
-    Assert.assertTrue(verifier.verify());
-
-    IdealState idealState =
-        _setupTool.getClusterManagementTool().getResourceIdealState(CLUSTER_NAME, testDb);
-    verifySemiAutoMasterSlaveAssignment(idealState);
-
-    // kill 1 node
-    _participants[0].syncStop();
-
-    Assert.assertTrue(verifier.verify());
-
-    idealState = _setupTool.getClusterManagementTool().getResourceIdealState(CLUSTER_NAME, testDb);
-    verifySemiAutoMasterSlaveAssignment(idealState);
-
-    // disable an instance
-    _setupTool.getClusterManagementTool()
-        .enableInstance(CLUSTER_NAME, _participants[1].getInstanceName(), false);
-    idealState = _setupTool.getClusterManagementTool().getResourceIdealState(CLUSTER_NAME, testDb);
-    verifySemiAutoMasterSlaveAssignment(idealState);
-
-    // clean up
-    _setupTool.getClusterManagementTool().dropResource(CLUSTER_NAME, testDb);
-    _setupTool.getClusterManagementTool()
-        .enableInstance(CLUSTER_NAME, _participants[1].getInstanceName(), true);
-    _participants[0].reset();
-    _participants[0].syncStart();
-  }
-
-  private void verifySemiAutoMasterSlaveAssignment(IdealState idealState) {
-    for (String partition : idealState.getPartitionSet()) {
-      Map<String, String> instanceStateMap = idealState.getInstanceStateMap(partition);
-      List<String> preferenceList = idealState.getPreferenceList(partition);
-      int numMaster = 0;
-
-      for (String ins : preferenceList) {
-        Assert.assertTrue(instanceStateMap.containsKey(ins));
-        String state = instanceStateMap.get(ins);
-        Assert.assertTrue(state.equals(MasterSlaveSMD.States.MASTER.name()) || state
-            .equals(MasterSlaveSMD.States.SLAVE.name()));
-        if (state.equals(MasterSlaveSMD.States.MASTER.name())) {
-          numMaster++;
-        }
-      }
-
-      Assert.assertEquals(numMaster, 1);
-    }
-  }
-
-  // verify that the disabled or failed instance should not be included in bestPossible assignment.
+  // verify that both list field and map field should be persisted in IS,
+  // And the disabled or failed instance should not be included in bestPossible assignment.
   private void verifyAssignmentInIdealStateWithPersistEnabled(IdealState idealState,
       Set<String> excludedInstances) {
     for (String partition : idealState.getPartitionSet()) {
@@ -228,8 +168,20 @@ public class TestRebalancerPersistAssignments extends ZkStandAloneCMTestBase {
       Assert.assertFalse(instanceStateMap.isEmpty());
 
       Set<String> instancesInMap = instanceStateMap.keySet();
-      Set<String> instanceInList = idealState.getInstanceSet(partition);
-      Assert.assertTrue(instanceInList.containsAll(instancesInMap));
+      if (idealState.getRebalanceMode() == RebalanceMode.SEMI_AUTO) {
+        Set<String> instanceInList = idealState.getInstanceSet(partition);
+        Assert.assertTrue(instanceInList.containsAll(instancesInMap));
+      }
+
+      if(idealState.getRebalanceMode() == RebalanceMode.FULL_AUTO) {
+        // preference list should be persisted in IS.
+        List<String> instanceList = idealState.getPreferenceList(partition);
+        Assert.assertNotNull(instanceList);
+        Assert.assertFalse(instanceList.isEmpty());
+        for (String ins : excludedInstances) {
+          Assert.assertFalse(instanceList.contains(ins));
+        }
+      }
 
       for (String ins : excludedInstances) {
         Assert.assertFalse(instancesInMap.contains(ins));
@@ -254,6 +206,12 @@ public class TestRebalancerPersistAssignments extends ZkStandAloneCMTestBase {
           // if at least one excluded instance is included, it means assignment was not updated.
           assignmentNotChanged = true;
         }
+        if(idealState.getRebalanceMode() == RebalanceMode.FULL_AUTO) {
+          List<String> instanceList = idealState.getPreferenceList(partition);
+          if (instanceList.contains(ins)) {
+            assignmentNotChanged = true;
+          }
+        }
       }
     }