You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by to...@apache.org on 2020/08/05 09:49:11 UTC

[hbase] 04/09: bugfixes (log split, etc), rebase fixes, fixed TestSCP*Meta* tests

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

toffer pushed a commit to branch HBASE-11288.branch-2
in repository https://gitbox.apache.org/repos/asf/hbase.git

commit c0e0161fc5af582231db9ecc3b16bc0cafd83c19
Author: Francis Liu <to...@apache.org>
AuthorDate: Tue Aug 4 00:59:54 2020 -0700

    bugfixes (log split, etc), rebase fixes, fixed TestSCP*Meta* tests
---
 .../org/apache/hadoop/hbase/MetaTableAccessor.java |  4 +-
 .../src/main/protobuf/MasterProcedure.proto        |  7 +-
 .../procedure/TestSCPWithReplicasWithRSGroup.java  |  2 +-
 .../coordination/ZkSplitLogWorkerCoordination.java | 20 +++--
 .../org/apache/hadoop/hbase/master/HMaster.java    | 38 +--------
 .../hbase/master/assignment/AssignmentManager.java | 47 +++++-----
 .../hbase/master/procedure/InitMetaProcedure.java  |  5 +-
 .../hbase/master/procedure/InitRootProcedure.java  | 99 ++++++++++++++++------
 .../master/procedure/ModifyTableProcedure.java     |  2 +-
 .../hbase/security/access/AccessChecker.java       |  4 +-
 .../master/assignment/AssignmentTestingUtil.java   | 14 +++
 .../procedure/MasterProcedureTestingUtility.java   |  2 +-
 .../hadoop/hbase/master/procedure/TestSCP.java     |  2 +-
 .../hadoop/hbase/master/procedure/TestSCPBase.java | 31 +++++--
 .../hbase/master/procedure/TestSCPWithMeta.java    |  9 +-
 .../hbase/master/procedure/TestSCPWithoutMeta.java |  2 +-
 16 files changed, 181 insertions(+), 107 deletions(-)

diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/MetaTableAccessor.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/MetaTableAccessor.java
index 226e5e7..fbf2208 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/MetaTableAccessor.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/MetaTableAccessor.java
@@ -1957,7 +1957,9 @@ public class MetaTableAccessor {
       prevCatalogTableName = currCatalogTableName;
       prevRegionInfo = hri;
     }
-    deleteFromCatalogTable(connection, prevCatalogTableName,  deletes);
+    if (deletes.size() > 0) {
+      deleteFromCatalogTable(connection, prevCatalogTableName, deletes);
+    }
     LOG.info("Deleted {} regions from {}", regionsInfo.size(), prevCatalogTableName);
     LOG.debug("Deleted regions: {}", regionsInfo);
   }
diff --git a/hbase-protocol-shaded/src/main/protobuf/MasterProcedure.proto b/hbase-protocol-shaded/src/main/protobuf/MasterProcedure.proto
index e27c5d4..a71c972 100644
--- a/hbase-protocol-shaded/src/main/protobuf/MasterProcedure.proto
+++ b/hbase-protocol-shaded/src/main/protobuf/MasterProcedure.proto
@@ -473,9 +473,10 @@ message ReopenTableRegionsStateData {
 }
 
 enum InitRootState{
-  INIT_ROOT_ASSIGN_ROOT = 1;
-  INIT_ROOT_LOAD_ROOT = 2;
-  INIT_ROOT_INIT_META = 3;
+  INIT_ROOT_WRITE_FS_LAYOUT = 1;
+  INIT_ROOT_ASSIGN_ROOT = 2;
+  INIT_ROOT_LOAD_ROOT = 3;
+  INIT_ROOT_INIT_META = 4;
 }
 
 message InitRootStateData {
diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCPWithReplicasWithRSGroup.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCPWithReplicasWithRSGroup.java
index 93739e7..cbf42e7 100644
--- a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCPWithReplicasWithRSGroup.java
+++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCPWithReplicasWithRSGroup.java
@@ -60,6 +60,6 @@ public class TestSCPWithReplicasWithRSGroup extends TestSCPBase {
     HMaster master = util.getHBaseCluster().getMaster();
     util.waitFor(60000, (Predicate<Exception>) () ->
         master.isInitialized() && ((RSGroupBasedLoadBalancer) master.getLoadBalancer()).isOnline());
-    testRecoveryAndDoubleExecution(false, false);
+    testRecoveryAndDoubleExecution(false, false, false);
   }
 }
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coordination/ZkSplitLogWorkerCoordination.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coordination/ZkSplitLogWorkerCoordination.java
index a8eabbd..1c81310 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coordination/ZkSplitLogWorkerCoordination.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coordination/ZkSplitLogWorkerCoordination.java
@@ -405,17 +405,27 @@ public class ZkSplitLogWorkerCoordination extends ZKListener implements
       int rootOffset = -1;
       for (int i = 0; i < paths.size(); i++) {
         if (AbstractFSWALProvider.isRootFile(paths.get(i))) {
-          offset = rootOffset;
+          rootOffset = i;
         }
         if (AbstractFSWALProvider.isMetaFile(paths.get(i))) {
-          offset = metaOffset;
+          metaOffset = i;
         }
       }
+      int seqIndex = 0;
+      int seq[] = new int[paths.size()];
       if (rootOffset != -1) {
-        offset = rootOffset;
+        seq[seqIndex] = rootOffset;
+        seqIndex++;
       }
       if (metaOffset != -1) {
-        offset = metaOffset;
+        seq[seqIndex] = metaOffset;
+        seqIndex++;
+      }
+      for (int i=0; i < seq.length; i++) {
+        if (i != rootOffset && i != metaOffset) {
+          seq[seqIndex] = i;
+          seqIndex++;
+        }
       }
       int numTasks = paths.size();
       boolean taskGrabbed = false;
@@ -426,7 +436,7 @@ public class ZkSplitLogWorkerCoordination extends ZKListener implements
               LOG.trace("Current region server " + server.getServerName()
                 + " is ready to take more tasks, will get task list and try grab tasks again.");
             }
-            int idx = (i + offset) % paths.size();
+            int idx = seq[i];
             // don't call ZKSplitLog.getNodeName() because that will lead to
             // double encoding of the path name
             taskGrabbed |= grabTask(ZNodePaths.joinZNode(
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
index 8e7aac2..4121022 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
@@ -1012,7 +1012,7 @@ public class HMaster extends HRegionServer implements MasterServices {
 
     // Checking if meta needs initializing.
     status.setStatus("Initializing catalog tables if this is a new deploy");
-    // Print out state of hbase:meta on startup; helps debugging.
+    // Print out state of hbase:root on startup; helps debugging.
     RegionState rs = this.assignmentManager.getRegionStates().
         getRegionState(RegionInfoBuilder.ROOT_REGIONINFO);
     LOG.info("hbase:root {}", rs);
@@ -1061,7 +1061,7 @@ public class HMaster extends HRegionServer implements MasterServices {
     }
 
     status.setStatus("Starting assignment manager");
-    this.assignmentManager.joinCluster();
+    this.assignmentManager.joinCluster(initRootProc == null);
     // The below depends on hbase:meta being online.
     this.tableStateManager.start();
     // Below has to happen after tablestatemanager has started in the case where this hbase-2.x
@@ -1198,39 +1198,7 @@ public class HMaster extends HRegionServer implements MasterServices {
   //TODO francis move this to AM
   @VisibleForTesting
   public boolean waitForMetaOnline() {
-    return isRegionOnline(RegionInfoBuilder.FIRST_META_REGIONINFO);
-  }
-
-  /**
-   * @return True if region is online and scannable else false if an error or shutdown (Otherwise
-   *   we just block in here holding up all forward-progess).
-   */
-  //TODO francis move this to AM
-  private boolean isRegionOnline(RegionInfo ri) {
-    RetryCounter rc = null;
-    while (!isStopped()) {
-      RegionState rs = this.assignmentManager.getRegionStates().getRegionState(ri);
-      if (rs.isOpened()) {
-        if (this.getServerManager().isServerOnline(rs.getServerName())) {
-          return true;
-        }
-      }
-      // Region is not OPEN.
-      Optional<Procedure<MasterProcedureEnv>> optProc = this.procedureExecutor.getProcedures().
-          stream().filter(p -> p instanceof ServerCrashProcedure).findAny();
-      // TODO: Add a page to refguide on how to do repair. Have this log message point to it.
-      // Page will talk about loss of edits, how to schedule at least the meta WAL recovery, and
-      // then how to assign including how to break region lock if one held.
-      LOG.warn("{} is NOT online; state={}; ServerCrashProcedures={}. Master startup cannot " +
-          "progress, in holding-pattern until region onlined.",
-          ri.getRegionNameAsString(), rs, optProc.isPresent());
-      // Check once-a-minute.
-      if (rc == null) {
-        rc = new RetryCounterFactory(1000).create();
-      }
-      Threads.sleep(rc.getBackoffTimeAndIncrementAttempts());
-    }
-    return false;
+    return assignmentManager.isRegionOnline(RegionInfoBuilder.FIRST_META_REGIONINFO);
   }
 
   /**
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java
index f847daf..2881f83 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java
@@ -229,7 +229,7 @@ public class AssignmentManager {
     // Start the Assignment Thread
     startAssignmentThread();
 
-    // load meta region state
+    // load root region state
     ZKWatcher zkw = master.getZooKeeper();
     // it could be null in some tests
     if (zkw != null) {
@@ -247,7 +247,7 @@ public class AssignmentManager {
       if (regionState.getServerName() != null) {
         regionStates.addRegionToServer(regionNode);
       }
-      setMetaAssigned(regionState.getRegion(), regionState.getState() == State.OPEN);
+      setRootAssigned(regionState.getRegion(), regionState.getState() == State.OPEN);
     }
   }
 
@@ -1578,7 +1578,14 @@ public class AssignmentManager {
   // ============================================================================================
   //  TODO: Master load/bootstrap
   // ============================================================================================
-  public void joinCluster() throws IOException {
+  public void joinCluster(boolean loadRoot) throws IOException {
+    joinCluster(loadRoot, true, true);
+  }
+
+  @VisibleForTesting
+  public void joinCluster(boolean loadRoot, boolean shouldWaitForRootOnline,
+    boolean shouldWaitForMetaOnline)
+    throws IOException {
     long startTime = System.nanoTime();
     LOG.debug("Joining cluster...");
 
@@ -1592,15 +1599,18 @@ public class AssignmentManager {
 
 
     LOG.debug("Waiting for hbase:root to be online.");
-    if (!waitForRootOnline()) {
+    if (shouldWaitForRootOnline && !waitForRootOnline()) {
       throw new IOException("Waited too long for hbase:root to be online");
     }
 
-    //load hbase:root to build regionstate for hbase:meta regions
-    loadRoot();
+    //could have been run in initrootproc already
+    if (loadRoot) {
+      //load hbase:root to build regionstate for hbase:meta regions
+      loadRoot();
+    }
 
     LOG.debug("Waiting for hbase:meta to be online.");
-    if (!waitForMetaOnline()) {
+    if (shouldWaitForMetaOnline && !waitForMetaOnline()) {
       throw new IOException("Waited too long for hbase:meta to be online");
     }
 
@@ -1726,27 +1736,26 @@ public class AssignmentManager {
   }
 
   public void loadRoot() throws IOException {
-    //TODO francis is the the right monitor lock to synchronize on?
-    synchronized (rootLoadEvent) {
-      if (!isRootLoaded()) {
-        // TODO: use a thread pool
-        LOG.debug("Loaded hbase:root");
-        regionStateStore.visitCatalogTable(TableName.ROOT_TABLE_NAME,
-            new RegionCatalogLoadingVisitor());
-        wakeRootLoadedEvent();
-      } else {
-        LOG.debug("Not loading hbase:root, already loaded");
-      }
+    if (!isRootLoaded()) {
+      // TODO: use a thread pool
+      LOG.debug("Loading hbase:root");
+      regionStateStore.visitCatalogTable(TableName.ROOT_TABLE_NAME,
+          new RegionCatalogLoadingVisitor());
+      wakeRootLoadedEvent();
+      LOG.debug("Loaded hbase:root");
+    } else {
+      LOG.debug("Not loading hbase:root, already loaded");
     }
   }
 
   private void loadMeta() throws IOException {
     // TODO: use a thread pool
-    LOG.debug("Loaded hbase:meta");
+    LOG.debug("Loading hbase:meta");
     regionStateStore.visitCatalogTable(TableName.META_TABLE_NAME,
         new RegionCatalogLoadingVisitor());
     // every assignment is blocked until meta is loaded.
     wakeMetaLoadedEvent();
+    LOG.debug("Loaded hbase:meta");
   }
 
   /**
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/InitMetaProcedure.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/InitMetaProcedure.java
index bbdaaa7..9a81839 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/InitMetaProcedure.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/InitMetaProcedure.java
@@ -78,7 +78,7 @@ public class InitMetaProcedure extends AbstractStateMachineTableProcedure<InitMe
     // created here in bootstrap and it'll need to be cleaned up. Better to
     // not make it in first place. Turn off block caching for bootstrap.
     // Enable after.
-    FSTableDescriptors.tryUpdateMetaTableDescriptor(conf, fs, rootDir,
+    FSTableDescriptors.tryUpdateCatalogTableDescriptor(conf, fs, rootDir,
       builder -> builder.setRegionReplication(
         conf.getInt(HConstants.META_REPLICAS_NUM, HConstants.DEFAULT_META_REPLICA_NUM)));
     TableDescriptor metaDescriptor = new FSTableDescriptors(conf).get(TableName.META_TABLE_NAME);
@@ -144,7 +144,8 @@ public class InitMetaProcedure extends AbstractStateMachineTableProcedure<InitMe
 
   @Override
   protected InitMetaState getInitialState() {
-    return InitMetaState.INIT_META_WRITE_FS_LAYOUT;
+    //TODO francis remove INIT_META_WRITE_FS_LAYOUT
+    return InitMetaState.INIT_META_ASSIGN_META;
   }
 
   @Override
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/InitRootProcedure.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/InitRootProcedure.java
index 4fe216b..6bb37cb 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/InitRootProcedure.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/InitRootProcedure.java
@@ -20,6 +20,9 @@ package org.apache.hadoop.hbase.master.procedure;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.concurrent.CountDownLatch;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.HRegionInfo;
 import org.apache.hadoop.hbase.KeyValue;
@@ -28,6 +31,7 @@ import org.apache.hadoop.hbase.client.Put;
 import org.apache.hadoop.hbase.client.RegionInfo;
 import org.apache.hadoop.hbase.client.RegionInfoBuilder;
 import org.apache.hadoop.hbase.client.Table;
+import org.apache.hadoop.hbase.client.TableDescriptor;
 import org.apache.hadoop.hbase.master.RegionState;
 import org.apache.hadoop.hbase.master.assignment.RegionStateStore;
 import org.apache.hadoop.hbase.master.assignment.TransitRegionStateProcedure;
@@ -35,9 +39,12 @@ import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer;
 import org.apache.hadoop.hbase.procedure2.ProcedureSuspendedException;
 import org.apache.hadoop.hbase.procedure2.ProcedureUtil;
 import org.apache.hadoop.hbase.procedure2.ProcedureYieldException;
+import org.apache.hadoop.hbase.regionserver.HRegion;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos;
 import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.CommonFSUtils;
 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
+import org.apache.hadoop.hbase.util.FSTableDescriptors;
 import org.apache.hadoop.hbase.util.RetryCounter;
 import org.apache.yetus.audience.InterfaceAudience;
 import org.slf4j.Logger;
@@ -69,37 +76,75 @@ public class InitRootProcedure extends AbstractStateMachineTableProcedure<InitRo
     return TableOperationType.CREATE;
   }
 
+  private static void writeFsLayout(Path rootDir, Configuration conf) throws IOException {
+    LOG.info("BOOTSTRAP: creating hbase:root region");
+    FileSystem fs = rootDir.getFileSystem(conf);
+    Path tableDir = CommonFSUtils.getTableDir(rootDir, TableName.ROOT_TABLE_NAME);
+    if (fs.exists(tableDir) && !fs.delete(tableDir, true)) {
+      LOG.warn("Can not delete partial created root table, continue...");
+    }
+    // Bootstrapping, make sure blockcache is off. Else, one will be
+    // created here in bootstrap and it'll need to be cleaned up. Better to
+    // not make it in first place. Turn off block caching for bootstrap.
+    // Enable after.
+    FSTableDescriptors.tryUpdateCatalogTableDescriptor(conf, fs, rootDir,
+      builder -> builder.setRegionReplication(
+        conf.getInt(HConstants.META_REPLICAS_NUM, HConstants.DEFAULT_META_REPLICA_NUM)));
+    TableDescriptor rootDescriptor = new FSTableDescriptors(conf).get(TableName.ROOT_TABLE_NAME);
+    HRegion
+      .createHRegion(RegionInfoBuilder.ROOT_REGIONINFO, rootDir, conf, rootDescriptor, null)
+      .close();
+  }
+
   @Override
   protected Flow executeFromState(MasterProcedureEnv env, InitRootState state)
       throws ProcedureSuspendedException, ProcedureYieldException, InterruptedException {
     LOG.debug("Execute {}", this);
-    switch (state) {
-      case INIT_ROOT_ASSIGN_ROOT:
-        LOG.info("Going to assign root");
-        addChildProcedure(env.getAssignmentManager()
-          .createAssignProcedures(Arrays.asList(RegionInfoBuilder.ROOT_REGIONINFO)));
-        setNextState(MasterProcedureProtos.InitRootState.INIT_ROOT_LOAD_ROOT);
-        return Flow.HAS_MORE_STATE;
-      case INIT_ROOT_LOAD_ROOT:
-        try {
-          addMetaRegionToRoot(env);
-          env.getAssignmentManager().loadRoot();
-        } catch (IOException e) {
-          if (retryCounter == null) {
-            retryCounter = ProcedureUtil.createRetryCounter(env.getMasterConfiguration());
+    try {
+      switch (state) {
+        case INIT_ROOT_WRITE_FS_LAYOUT:
+          Configuration conf = env.getMasterConfiguration();
+          Path rootDir = CommonFSUtils.getRootDir(conf);
+          writeFsLayout(rootDir, conf);
+          setNextState(MasterProcedureProtos.InitRootState.INIT_ROOT_ASSIGN_ROOT);
+          return Flow.HAS_MORE_STATE;
+        case INIT_ROOT_ASSIGN_ROOT:
+          LOG.info("Going to assign root");
+          addChildProcedure(env.getAssignmentManager()
+            .createAssignProcedures(Arrays.asList(RegionInfoBuilder.ROOT_REGIONINFO)));
+          setNextState(MasterProcedureProtos.InitRootState.INIT_ROOT_LOAD_ROOT);
+          return Flow.HAS_MORE_STATE;
+        case INIT_ROOT_LOAD_ROOT:
+          try {
+            addMetaRegionToRoot(env);
+            env.getAssignmentManager().loadRoot();
+          } catch (IOException e) {
+            if (retryCounter == null) {
+              retryCounter = ProcedureUtil.createRetryCounter(env.getMasterConfiguration());
+            }
+            long backoff = retryCounter.getBackoffTimeAndIncrementAttempts();
+            LOG.warn("Failed to init default and system namespaces, suspend {}secs", backoff, e);
+            setTimeout(Math.toIntExact(backoff));
+            setState(ProcedureProtos.ProcedureState.WAITING_TIMEOUT);
+            skipPersistence();
+            throw new ProcedureSuspendedException();
           }
-          long backoff = retryCounter.getBackoffTimeAndIncrementAttempts();
-          LOG.warn("Failed to init default and system namespaces, suspend {}secs", backoff, e);
-          setTimeout(Math.toIntExact(backoff));
-          setState(ProcedureProtos.ProcedureState.WAITING_TIMEOUT);
-          skipPersistence();
-          throw new ProcedureSuspendedException();
-        }
-      case INIT_ROOT_INIT_META:
-        addChildProcedure(new InitMetaProcedure());
-        return Flow.NO_MORE_STATE;
-      default:
-        throw new UnsupportedOperationException("unhandled state=" + state);
+        case INIT_ROOT_INIT_META:
+          addChildProcedure(new InitMetaProcedure());
+          return Flow.NO_MORE_STATE;
+        default:
+          throw new UnsupportedOperationException("unhandled state=" + state);
+      }
+    } catch (IOException e) {
+      if (retryCounter == null) {
+        retryCounter = ProcedureUtil.createRetryCounter(env.getMasterConfiguration());
+      }
+      long backoff = retryCounter.getBackoffTimeAndIncrementAttempts();
+      LOG.warn("Failed to init meta, suspend {}secs", backoff, e);
+      setTimeout(Math.toIntExact(backoff));
+      setState(ProcedureProtos.ProcedureState.WAITING_TIMEOUT);
+      skipPersistence();
+      throw new ProcedureSuspendedException();
     }
   }
 
@@ -142,7 +187,7 @@ public class InitRootProcedure extends AbstractStateMachineTableProcedure<InitRo
 
   @Override
   protected InitRootState getInitialState() {
-    return InitRootState.INIT_ROOT_ASSIGN_ROOT;
+    return InitRootState.INIT_ROOT_WRITE_FS_LAYOUT;
   }
 
   @Override
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyTableProcedure.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyTableProcedure.java
index 286d699..a3df957 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyTableProcedure.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/ModifyTableProcedure.java
@@ -405,7 +405,7 @@ public class ModifyTableProcedure
           newReplicaCount,
           oldReplicaCount - newReplicaCount,
           connection,
-          newTableDescriptor.getTableName());
+          TableName.META_TABLE_NAME);
       }
     }
     if (newReplicaCount > oldReplicaCount) {
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessChecker.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessChecker.java
index 7282a1f..0c8c305 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessChecker.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessChecker.java
@@ -556,9 +556,9 @@ public class AccessChecker {
    */
   public AuthResult permissionGranted(String request, User user, Action permRequest,
       TableName tableName, Map<byte[], ? extends Collection<?>> families) {
-    // 1. All users need read access to hbase:meta table.
+    // 1. All users need read access to hbase:root and hbase:meta table.
     // this is a very common operation, so deal with it quickly.
-    if (TableName.META_TABLE_NAME.equals(tableName)) {
+    if (TableName.ROOT_TABLE_NAME.equals(tableName) || TableName.META_TABLE_NAME.equals(tableName)) {
       if (permRequest == Action.READ) {
         return AuthResult.allow(request, "All users allowed", user, permRequest, tableName,
           families);
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/AssignmentTestingUtil.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/AssignmentTestingUtil.java
index 65001e2..7480567 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/AssignmentTestingUtil.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/AssignmentTestingUtil.java
@@ -109,6 +109,20 @@ public final class AssignmentTestingUtil {
     return serverName;
   }
 
+  public static boolean isServerHoldingRoot(final HBaseTestingUtility util,
+    final ServerName serverName) throws Exception {
+    for (RegionInfo hri: getRootRegions(util)) {
+      if (serverName.equals(getServerHoldingRegion(util, hri))) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public static Set<RegionInfo> getRootRegions(final HBaseTestingUtility util) {
+    return getMaster(util).getAssignmentManager().getRootRegionSet();
+  }
+
   public static boolean isServerHoldingMeta(final HBaseTestingUtility util,
       final ServerName serverName) throws Exception {
     for (RegionInfo hri: getMetaRegions(util)) {
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/MasterProcedureTestingUtility.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/MasterProcedureTestingUtility.java
index c60a604..2c80247 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/MasterProcedureTestingUtility.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/MasterProcedureTestingUtility.java
@@ -112,7 +112,7 @@ public class MasterProcedureTestingUtility {
         public Void call() throws Exception {
           AssignmentManager am = env.getAssignmentManager();
           try {
-            am.joinCluster();
+            am.joinCluster(true, false, false);
             master.setInitialized(true);
           } catch (Exception e) {
             LOG.warn("Failed to load meta", e);
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCP.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCP.java
index 222db70..b5a25ce 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCP.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCP.java
@@ -48,7 +48,7 @@ public class TestSCP extends TestSCPBase {
 
   @Test
   public void testCrashTargetRs() throws Exception {
-    testRecoveryAndDoubleExecution(false, false);
+    testRecoveryAndDoubleExecution(false, false,false);
   }
 
   @Test
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCPBase.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCPBase.java
index b3429f2..c443571 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCPBase.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCPBase.java
@@ -26,6 +26,7 @@ import org.apache.hadoop.hbase.MiniHBaseCluster;
 import org.apache.hadoop.hbase.ServerName;
 import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.client.RegionInfo;
+import org.apache.hadoop.hbase.client.RegionInfoBuilder;
 import org.apache.hadoop.hbase.client.RegionReplicaTestHelper;
 import org.apache.hadoop.hbase.client.Table;
 import org.apache.hadoop.hbase.master.HMaster;
@@ -75,10 +76,11 @@ public class TestSCPBase {
    * Run server crash procedure steps twice to test idempotency and that we are persisting all
    * needed state.
    */
-  protected void testRecoveryAndDoubleExecution(boolean carryingMeta, boolean doubleExecution)
+  protected void testRecoveryAndDoubleExecution(
+    boolean carryingRoot, boolean carryingMeta, boolean doubleExecution)
       throws Exception {
-    final TableName tableName = TableName.valueOf("testRecoveryAndDoubleExecution-carryingMeta-" +
-      carryingMeta + "-doubleExecution-" + doubleExecution);
+    final TableName tableName = TableName.valueOf("testRecoveryAndDoubleExecution-carryingRoot-" +
+      carryingRoot + "-doubleExecution-" + doubleExecution);
     try (Table t = createTable(tableName)) {
       // Load the table with a bit of data so some logs to split and some edits in each region.
       this.util.loadTable(t, HBaseTestingUtility.COLUMNS[0]);
@@ -90,14 +92,31 @@ public class TestSCPBase {
       final HMaster master = this.util.getHBaseCluster().getMaster();
       final ProcedureExecutor<MasterProcedureEnv> procExec = master.getMasterProcedureExecutor();
       // find the first server that match the request and executes the test
-      ServerName rsToKill = null;
+      ServerName otherRS = null;
+      ServerName rootRs = null;
+      ServerName metaRs = null;
       for (RegionInfo hri : util.getAdmin().getRegions(tableName)) {
         final ServerName serverName = AssignmentTestingUtil.getServerHoldingRegion(util, hri);
+        if (AssignmentTestingUtil.isServerHoldingRoot(util, serverName) == carryingRoot) {
+          rootRs = serverName;
+        }
         if (AssignmentTestingUtil.isServerHoldingMeta(util, serverName) == carryingMeta) {
-          rsToKill = serverName;
-          break;
+          metaRs = serverName;
+        }
+        if (!AssignmentTestingUtil.isServerHoldingRoot(util, serverName) &&
+          !AssignmentTestingUtil.isServerHoldingMeta(util, serverName)) {
+          otherRS = serverName;
         }
       }
+      ServerName rsToKill = rootRs;
+      if (carryingRoot) {
+        util.moveRegionAndWait(RegionInfoBuilder.FIRST_META_REGIONINFO, otherRS);
+      } else if (carryingMeta) {
+        util.moveRegionAndWait(RegionInfoBuilder.ROOT_REGIONINFO, otherRS);
+        rsToKill = metaRs;
+      } else {
+        rsToKill = otherRS;
+      }
       // Enable test flags and then queue the crash procedure.
       ProcedureTestingUtility.waitNoProcedureRunning(procExec);
       if (doubleExecution) {
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCPWithMeta.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCPWithMeta.java
index 09d9d87..d7e6984 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCPWithMeta.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCPWithMeta.java
@@ -32,7 +32,12 @@ public class TestSCPWithMeta extends TestSCPBase {
     HBaseClassTestRule.forClass(TestSCPWithMeta.class);
 
   @Test
-  public void testRecoveryAndDoubleExecutionOnRsWithMeta() throws Exception {
-    testRecoveryAndDoubleExecution(true, true);
+  public void testRecoveryAndDoubleExecutionOnRsWithRoot() throws Exception {
+    testRecoveryAndDoubleExecution(true, false, true);
   }
+
+//  @Test(timeout = 900000 )
+//  public void testRecoveryAndDoubleExecutionOnRsWithMeta() throws Exception {
+//    testRecoveryAndDoubleExecution(false, true, true);
+//  }
 }
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCPWithoutMeta.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCPWithoutMeta.java
index 3347725..ff97b05 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCPWithoutMeta.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSCPWithoutMeta.java
@@ -33,6 +33,6 @@ public class TestSCPWithoutMeta extends TestSCPBase {
 
   @Test
   public void testRecoveryAndDoubleExecutionOnRsWithoutMeta() throws Exception {
-    testRecoveryAndDoubleExecution(false, true);
+    testRecoveryAndDoubleExecution(false, false,true);
   }
 }