Repository: hbase
Updated Branches:
  refs/heads/master 00b302435 -> 0a2407784


http://git-wip-us.apache.org/repos/asf/hbase/blob/0a240778/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java
index 85b7967..07e16c8 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java
@@ -96,8 +96,8 @@ import 
org.apache.hadoop.hbase.ipc.ServerNotRunningYetException;
 import org.apache.hadoop.hbase.ipc.ServerRpcController;
 import org.apache.hadoop.hbase.master.MasterRpcServices;
 import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.AdminService;
-import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CloseRegionForSplitRequest;
-import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CloseRegionForSplitResponse;
+import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CloseRegionForSplitOrMergeRequest;
+import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CloseRegionForSplitOrMergeResponse;
 import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CloseRegionRequest;
 import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CloseRegionResponse;
 import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CompactRegionRequest;
@@ -1384,25 +1384,28 @@ public class RSRpcServices implements 
HBaseRPCErrorHandler,
 
   @Override
   @QosPriority(priority=HConstants.ADMIN_QOS)
-  public CloseRegionForSplitResponse closeRegionForSplit(
+  public CloseRegionForSplitOrMergeResponse closeRegionForSplitOrMerge(
       final RpcController controller,
-      final CloseRegionForSplitRequest request) throws ServiceException {
+      final CloseRegionForSplitOrMergeRequest request) throws ServiceException 
{
     try {
       checkOpen();
 
-      final String encodedRegionName = 
ProtobufUtil.getRegionEncodedName(request.getRegion());
+      List<String> encodedRegionNameList = new ArrayList<>();
+      for(int i = 0; i < request.getRegionCount(); i++) {
+        final String encodedRegionName = 
ProtobufUtil.getRegionEncodedName(request.getRegion(i));
 
-      // Can be null if we're calling close on a region that's not online
-      final Region parentRegion = 
regionServer.getFromOnlineRegions(encodedRegionName);
-      if ((parentRegion != null) && (parentRegion.getCoprocessorHost() != 
null)) {
-        parentRegion.getCoprocessorHost().preClose(false);
+        // Can be null if we're calling close on a region that's not online
+        final Region targetRegion = 
regionServer.getFromOnlineRegions(encodedRegionName);
+        if ((targetRegion != null) && (targetRegion.getCoprocessorHost() != 
null)) {
+          targetRegion.getCoprocessorHost().preClose(false);
+          encodedRegionNameList.add(encodedRegionName);
+        }
       }
-
       requestCount.increment();
-      LOG.info("Close and offline " + encodedRegionName + " and prepare for 
split.");
-      boolean closed = 
regionServer.closeAndOfflineRegionForSplit(encodedRegionName);
-      CloseRegionForSplitResponse.Builder builder =
-          CloseRegionForSplitResponse.newBuilder().setClosed(closed);
+      LOG.info("Close and offline " + encodedRegionNameList + " regions.");
+      boolean closed = 
regionServer.closeAndOfflineRegionForSplitOrMerge(encodedRegionNameList);
+      CloseRegionForSplitOrMergeResponse.Builder builder =
+          CloseRegionForSplitOrMergeResponse.newBuilder().setClosed(closed);
       return builder.build();
     } catch (IOException ie) {
       throw new ServiceException(ie);

http://git-wip-us.apache.org/repos/asf/hbase/blob/0a240778/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAdmin1.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAdmin1.java 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAdmin1.java
index 2af65a4..65cedda 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAdmin1.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAdmin1.java
@@ -30,7 +30,6 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -1180,10 +1179,10 @@ public class TestAdmin1 {
     gotException = false;
     // Try merging a replica with another. Should fail.
     try {
-      // TODO convert this to version that is synchronous (See HBASE-16668)
-      
TEST_UTIL.getAdmin().mergeRegionsAsync(regions.get(1).getFirst().getEncodedNameAsBytes(),
-          regions.get(2).getFirst().getEncodedNameAsBytes(), true)
-          .get(60, TimeUnit.SECONDS);
+      TEST_UTIL.getHBaseAdmin().mergeRegionsSync(
+        regions.get(1).getFirst().getEncodedNameAsBytes(),
+        regions.get(2).getFirst().getEncodedNameAsBytes(),
+        true);
     } catch (IllegalArgumentException m) {
       gotException = true;
     }

http://git-wip-us.apache.org/repos/asf/hbase/blob/0a240778/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java
index 5497a3f..7522e85 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java
@@ -175,6 +175,8 @@ public class TestMasterObserver {
     private boolean preGetTableNamesCalled;
     private boolean preDispatchMergeCalled;
     private boolean postDispatchMergeCalled;
+    private boolean preMergeRegionsCalled;
+    private boolean postMergeRegionsCalled;
 
     public void enableBypass(boolean bypass) {
       this.bypass = bypass;
@@ -261,6 +263,26 @@ public class TestMasterObserver {
       preGetTableNamesCalled = false;
       preDispatchMergeCalled = false;
       postDispatchMergeCalled = false;
+      preMergeRegionsCalled = false;
+      postMergeRegionsCalled = false;
+    }
+
+    @Override
+    public void preMergeRegions(
+        final ObserverContext<MasterCoprocessorEnvironment> ctx,
+        final HRegionInfo[] regionsToMerge) throws IOException {
+      preMergeRegionsCalled = true;
+    }
+
+    @Override
+    public void postMergeRegions(
+        final ObserverContext<MasterCoprocessorEnvironment> ctx,
+        final HRegionInfo[] regionsToMerge) throws IOException {
+      postMergeRegionsCalled = true;
+    }
+
+    public boolean wasMergeRegionsCalled() {
+      return preMergeRegionsCalled && postMergeRegionsCalled;
     }
 
     @Override
@@ -1512,9 +1534,42 @@ public class TestMasterObserver {
     }
 
     @Override
-    public void preRollBackSplitRegionAction(
+    public void postRollBackSplitRegionAction(
         final ObserverContext<MasterCoprocessorEnvironment> ctx) throws 
IOException {
     }
+
+    @Override
+    public void preMergeRegionsAction(
+        final ObserverContext<MasterCoprocessorEnvironment> ctx,
+        final HRegionInfo[] regionsToMerge) throws IOException {
+    }
+
+    @Override
+    public void postCompletedMergeRegionsAction(
+        final ObserverContext<MasterCoprocessorEnvironment> c,
+        final HRegionInfo[] regionsToMerge,
+        final HRegionInfo mergedRegion) throws IOException {
+    }
+
+    @Override
+    public void preMergeRegionsCommitAction(
+        final ObserverContext<MasterCoprocessorEnvironment> ctx,
+        final HRegionInfo[] regionsToMerge,
+        final List<Mutation> metaEntries) throws IOException {
+    }
+
+    @Override
+    public void postMergeRegionsCommitAction(
+        final ObserverContext<MasterCoprocessorEnvironment> ctx,
+        final HRegionInfo[] regionsToMerge,
+        final HRegionInfo mergedRegion) throws IOException {
+    }
+
+    @Override
+    public void postRollBackMergeRegionsAction(
+        final ObserverContext<MasterCoprocessorEnvironment> ctx,
+        final HRegionInfo[] regionsToMerge) throws IOException {
+    }
   }
 
   private static HBaseTestingUtility UTIL = new HBaseTestingUtility();
@@ -1593,7 +1648,7 @@ public class TestMasterObserver {
       
admin.mergeRegionsAsync(regions.get(0).getRegionInfo().getEncodedNameAsBytes(),
         regions.get(1).getRegionInfo().getEncodedNameAsBytes(), true);
       assertTrue("Coprocessor should have been called on region merge",
-        cp.wasDispatchMergeCalled());
+        cp.wasMergeRegionsCalled());
 
       tableCreationLatch = new CountDownLatch(1);
       admin.disableTable(tableName);

http://git-wip-us.apache.org/repos/asf/hbase/blob/0a240778/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionServerObserver.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionServerObserver.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionServerObserver.java
index ecf9da1..4a62bff 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionServerObserver.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionServerObserver.java
@@ -46,6 +46,7 @@ import 
org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost;
 import org.apache.hadoop.hbase.testclassification.CoprocessorTests;
 import org.apache.hadoop.hbase.testclassification.MediumTests;
 import org.apache.hadoop.hbase.util.Bytes;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 
@@ -61,6 +62,7 @@ public class TestRegionServerObserver {
    * Test verifies the hooks in regions merge.
    * @throws Exception
    */
+  @Ignore
   @Test
   public void testCoprocessorHooksInRegionsMerge() throws Exception {
     final int NUM_MASTERS = 1;

http://git-wip-us.apache.org/repos/asf/hbase/blob/0a240778/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockNoopMasterServices.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockNoopMasterServices.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockNoopMasterServices.java
index 2630068..b52f5df 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockNoopMasterServices.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockNoopMasterServices.java
@@ -287,6 +287,15 @@ public class MockNoopMasterServices implements 
MasterServices, Server {
   }
 
   @Override
+  public long mergeRegions(
+      final HRegionInfo[] regionsToMerge,
+      final boolean forcible,
+      final long nonceGroup,
+      final long nonce) throws IOException {
+    return -1;
+  }
+
+  @Override
   public long splitRegion(
       final HRegionInfo regionInfo,
       final byte[] splitRow,

http://git-wip-us.apache.org/repos/asf/hbase/blob/0a240778/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockRegionServer.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockRegionServer.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockRegionServer.java
index 1594b6d..ec8054e 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockRegionServer.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/MockRegionServer.java
@@ -51,8 +51,8 @@ import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos;
 import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CloseRegionRequest;
 import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CloseRegionResponse;
-import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CloseRegionForSplitRequest;
-import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CloseRegionForSplitResponse;
+import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CloseRegionForSplitOrMergeRequest;
+import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CloseRegionForSplitOrMergeResponse;
 import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CompactRegionRequest;
 import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CompactRegionResponse;
 import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.FlushRegionRequest;
@@ -500,9 +500,9 @@ ClientProtos.ClientService.BlockingInterface, 
RegionServerServices {
   }
 
   @Override
-  public CloseRegionForSplitResponse closeRegionForSplit(
+  public CloseRegionForSplitOrMergeResponse closeRegionForSplitOrMerge(
       RpcController controller,
-      CloseRegionForSplitRequest request) throws ServiceException {
+      CloseRegionForSplitOrMergeRequest request) throws ServiceException {
     return null;
   }
 

http://git-wip-us.apache.org/repos/asf/hbase/blob/0a240778/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestMergeTableRegionsProcedure.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestMergeTableRegionsProcedure.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestMergeTableRegionsProcedure.java
new file mode 100644
index 0000000..1915f69
--- /dev/null
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestMergeTableRegionsProcedure.java
@@ -0,0 +1,263 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.master.procedure;
+
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.Admin;
+import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
+import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
+import 
org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.MergeTableRegionsState;
+import org.apache.hadoop.hbase.testclassification.MasterTests;
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+@Category({MasterTests.class, MediumTests.class})
+public class TestMergeTableRegionsProcedure {
+  private static final Log LOG = 
LogFactory.getLog(TestMergeTableRegionsProcedure.class);
+
+  protected static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
+  private static long nonceGroup = HConstants.NO_NONCE;
+  private static long nonce = HConstants.NO_NONCE;
+
+  private static final int initialRegionCount = 4;
+  private final static byte[] FAMILY = Bytes.toBytes("FAMILY");
+  final static Configuration conf = UTIL.getConfiguration();
+  private static Admin admin;
+
+  private static void setupConf(Configuration conf) {
+    // Reduce the maximum attempts to speed up the test
+    conf.setInt("hbase.assignment.maximum.attempts", 3);
+    conf.setInt("hbase.master.maximum.ping.server.attempts", 3);
+    conf.setInt("hbase.master.ping.server.retry.sleep.interval", 1);
+
+    conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1);
+  }
+
+  @BeforeClass
+  public static void setupCluster() throws Exception {
+    setupConf(conf);
+    UTIL.startMiniCluster(1);
+    admin = UTIL.getHBaseAdmin();
+  }
+
+  @AfterClass
+  public static void cleanupTest() throws Exception {
+    try {
+      UTIL.shutdownMiniCluster();
+    } catch (Exception e) {
+      LOG.warn("failure shutting down cluster", e);
+    }
+  }
+
+  @Before
+  public void setup() throws Exception {
+    resetProcExecutorTestingKillFlag();
+    nonceGroup =
+        
MasterProcedureTestingUtility.generateNonceGroup(UTIL.getHBaseCluster().getMaster());
+    nonce = 
MasterProcedureTestingUtility.generateNonce(UTIL.getHBaseCluster().getMaster());
+    // Turn off balancer so it doesn't cut in and mess up our placements.
+    UTIL.getHBaseAdmin().setBalancerRunning(false, true);
+    // Turn off the meta scanner so it don't remove parent on us.
+    UTIL.getHBaseCluster().getMaster().setCatalogJanitorEnabled(false);
+    resetProcExecutorTestingKillFlag();
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    resetProcExecutorTestingKillFlag();
+    for (HTableDescriptor htd: UTIL.getHBaseAdmin().listTables()) {
+      LOG.info("Tear down, remove table=" + htd.getTableName());
+      UTIL.deleteTable(htd.getTableName());
+    }
+  }
+
+  private void resetProcExecutorTestingKillFlag() {
+    final ProcedureExecutor<MasterProcedureEnv> procExec = 
getMasterProcedureExecutor();
+    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false);
+    assertTrue("expected executor to be running", procExec.isRunning());
+  }
+
+  /**
+   * This tests two region merges
+   */
+  @Test(timeout=60000)
+  public void testMergeTwoRegions() throws Exception {
+    final TableName tableName = TableName.valueOf("testMergeTwoRegions");
+    final ProcedureExecutor<MasterProcedureEnv> procExec = 
getMasterProcedureExecutor();
+
+    List<HRegionInfo> tableRegions = createTable(tableName);
+
+    HRegionInfo[] regionsToMerge = new HRegionInfo[2];
+    regionsToMerge[0] = tableRegions.get(0);
+    regionsToMerge[1] = tableRegions.get(1);
+
+    long procId = procExec.submitProcedure(new MergeTableRegionsProcedure(
+      procExec.getEnvironment(), regionsToMerge, true));
+    ProcedureTestingUtility.waitProcedure(procExec, procId);
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
+
+    assertRegionCount(tableName, initialRegionCount - 1);
+  }
+
+  /**
+   * This tests two concurrent region merges
+   */
+  @Test(timeout=60000)
+  public void testMergeRegionsConcurrently() throws Exception {
+    final TableName tableName = 
TableName.valueOf("testMergeRegionsConcurrently");
+    final ProcedureExecutor<MasterProcedureEnv> procExec = 
getMasterProcedureExecutor();
+
+    List<HRegionInfo> tableRegions = createTable(tableName);
+
+    HRegionInfo[] regionsToMerge1 = new HRegionInfo[2];
+    HRegionInfo[] regionsToMerge2 = new HRegionInfo[2];
+    regionsToMerge1[0] = tableRegions.get(0);
+    regionsToMerge1[1] = tableRegions.get(1);
+    regionsToMerge2[0] = tableRegions.get(2);
+    regionsToMerge2[1] = tableRegions.get(3);
+
+    long procId1 = procExec.submitProcedure(new MergeTableRegionsProcedure(
+      procExec.getEnvironment(), regionsToMerge1, true));
+    long procId2 = procExec.submitProcedure(new MergeTableRegionsProcedure(
+      procExec.getEnvironment(), regionsToMerge2, true));
+    ProcedureTestingUtility.waitProcedure(procExec, procId1);
+    ProcedureTestingUtility.waitProcedure(procExec, procId2);
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId1);
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId2);
+    assertRegionCount(tableName, initialRegionCount - 2);
+  }
+
+  @Test(timeout=60000)
+  public void testMergeRegionsTwiceWithSameNonce() throws Exception {
+    final TableName tableName = 
TableName.valueOf("testMergeRegionsTwiceWithSameNonce");
+    final ProcedureExecutor<MasterProcedureEnv> procExec = 
getMasterProcedureExecutor();
+
+    List<HRegionInfo> tableRegions = createTable(tableName);
+
+    HRegionInfo[] regionsToMerge = new HRegionInfo[2];
+    regionsToMerge[0] = tableRegions.get(0);
+    regionsToMerge[1] = tableRegions.get(1);
+
+    long procId1 = procExec.submitProcedure(new MergeTableRegionsProcedure(
+      procExec.getEnvironment(), regionsToMerge, true), nonceGroup, nonce);
+    long procId2 = procExec.submitProcedure(new MergeTableRegionsProcedure(
+      procExec.getEnvironment(), regionsToMerge, true), nonceGroup, nonce);
+    assertEquals(procId1, procId2);
+
+    ProcedureTestingUtility.waitProcedure(procExec, procId1);
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId1);
+    // The second proc should succeed too - because it is the same proc.
+    ProcedureTestingUtility.waitProcedure(procExec, procId2);
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId2);
+
+    assertRegionCount(tableName, initialRegionCount - 1);
+  }
+
+  @Test(timeout=60000)
+  public void testRecoveryAndDoubleExecution() throws Exception {
+    final TableName tableName = 
TableName.valueOf("testRecoveryAndDoubleExecution");
+    final ProcedureExecutor<MasterProcedureEnv> procExec = 
getMasterProcedureExecutor();
+
+    List<HRegionInfo> tableRegions = createTable(tableName);
+
+    ProcedureTestingUtility.waitNoProcedureRunning(procExec);
+    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
+
+    HRegionInfo[] regionsToMerge = new HRegionInfo[2];
+    regionsToMerge[0] = tableRegions.get(0);
+    regionsToMerge[1] = tableRegions.get(1);
+
+    long procId = procExec.submitProcedure(
+      new MergeTableRegionsProcedure(procExec.getEnvironment(), 
regionsToMerge, true));
+
+    // Restart the executor and execute the step twice
+    int numberOfSteps = MergeTableRegionsState.values().length;
+    MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(procExec, 
procId, numberOfSteps);
+    ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
+
+    assertRegionCount(tableName, initialRegionCount - 1);
+  }
+
+  @Test(timeout = 60000)
+  public void testRollbackAndDoubleExecution() throws Exception {
+    final TableName tableName = 
TableName.valueOf("testRollbackAndDoubleExecution");
+    final ProcedureExecutor<MasterProcedureEnv> procExec = 
getMasterProcedureExecutor();
+
+    List<HRegionInfo> tableRegions = createTable(tableName);
+
+    ProcedureTestingUtility.waitNoProcedureRunning(procExec);
+    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
+
+    HRegionInfo[] regionsToMerge = new HRegionInfo[2];
+    regionsToMerge[0] = tableRegions.get(0);
+    regionsToMerge[1] = tableRegions.get(1);
+
+    long procId = procExec.submitProcedure(
+      new MergeTableRegionsProcedure(procExec.getEnvironment(), 
regionsToMerge, true));
+
+    // Failing before MERGE_TABLE_REGIONS_UPDATE_META we should trigger the 
rollback
+    // NOTE: the 6 (number before MERGE_TABLE_REGIONS_UPDATE_META step) is
+    // hardcoded, so you have to look at this test at least once when you add 
a new step.
+    int numberOfSteps = 6;
+    MasterProcedureTestingUtility.testRollbackAndDoubleExecution(procExec, 
procId, numberOfSteps);
+  }
+
+  private List<HRegionInfo> createTable(final TableName tableName)
+      throws Exception {
+    HTableDescriptor desc = new HTableDescriptor(tableName);
+    desc.addFamily(new HColumnDescriptor(FAMILY));
+    byte[][] splitRows = new byte[initialRegionCount - 1][];
+    for (int i = 0; i < splitRows.length; ++i) {
+      splitRows[i] = Bytes.toBytes(String.format("%d", i));
+    }
+    admin.createTable(desc, splitRows);
+    return assertRegionCount(tableName, initialRegionCount);
+  }
+
+  public List<HRegionInfo> assertRegionCount(final TableName tableName, final 
int nregions)
+      throws Exception {
+    UTIL.waitUntilNoRegionsInTransition();
+    List<HRegionInfo> tableRegions = admin.getTableRegions(tableName);
+    assertEquals(nregions, tableRegions.size());
+    return tableRegions;
+  }
+
+  private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
+    return UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor();
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/0a240778/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSplitTableRegionProcedure.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSplitTableRegionProcedure.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSplitTableRegionProcedure.java
index fcce0fb..55e38ba 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSplitTableRegionProcedure.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/procedure/TestSplitTableRegionProcedure.java
@@ -35,7 +35,6 @@ import org.apache.hadoop.hbase.HBaseTestingUtility;
 import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.HRegionInfo;
 import org.apache.hadoop.hbase.HTableDescriptor;
-import org.apache.hadoop.hbase.ProcedureInfo;
 import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.Waiter;
 import org.apache.hadoop.hbase.client.CompactionState;
@@ -311,8 +310,6 @@ public class TestSplitTableRegionProcedure {
     HRegionInfo [] regions = MasterProcedureTestingUtility.createTable(
       procExec, tableName, null, ColumnFamilyName1, ColumnFamilyName2);
     insertData(tableName);
-    int splitRowNum = startRowNum + rowCount / 2;
-    byte[] splitKey = Bytes.toBytes("" + splitRowNum);
 
     assertTrue("not able to find a splittable region", regions != null);
     assertTrue("not able to find a splittable region", regions.length == 1);
@@ -330,7 +327,7 @@ public class TestSplitTableRegionProcedure {
     }
   }
 
-  @Test(timeout = 600000)
+  @Test(timeout = 60000)
   public void testRollbackAndDoubleExecution() throws Exception {
     final TableName tableName = 
TableName.valueOf("testRollbackAndDoubleExecution");
     final ProcedureExecutor<MasterProcedureEnv> procExec = 
getMasterProcedureExecutor();

http://git-wip-us.apache.org/repos/asf/hbase/blob/0a240778/hbase-server/src/test/java/org/apache/hadoop/hbase/namespace/TestNamespaceAuditor.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/namespace/TestNamespaceAuditor.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/namespace/TestNamespaceAuditor.java
index a25c157..f8a1a93 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/namespace/TestNamespaceAuditor.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/namespace/TestNamespaceAuditor.java
@@ -66,6 +66,7 @@ import 
org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment;
 import org.apache.hadoop.hbase.coprocessor.RegionServerObserver;
 import org.apache.hadoop.hbase.mapreduce.TableInputFormatBase;
 import org.apache.hadoop.hbase.master.HMaster;
+import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
 import org.apache.hadoop.hbase.master.RegionState;
 import org.apache.hadoop.hbase.master.RegionStates;
 import org.apache.hadoop.hbase.master.TableNamespaceManager;
@@ -73,9 +74,7 @@ import org.apache.hadoop.hbase.quotas.MasterQuotaManager;
 import org.apache.hadoop.hbase.quotas.QuotaExceededException;
 import org.apache.hadoop.hbase.quotas.QuotaUtil;
 import org.apache.hadoop.hbase.regionserver.HRegion;
-import org.apache.hadoop.hbase.regionserver.HRegionServer;
 import org.apache.hadoop.hbase.regionserver.Region;
-import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost;
 import org.apache.hadoop.hbase.regionserver.Store;
 import org.apache.hadoop.hbase.regionserver.StoreFile;
 import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
@@ -104,11 +103,11 @@ public class TestNamespaceAuditor {
 
   @BeforeClass
   public static void before() throws Exception {
-    UTIL.getConfiguration().set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
-      CustomObserver.class.getName());
-    UTIL.getConfiguration().set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
-      MasterSyncObserver.class.getName());
     Configuration conf = UTIL.getConfiguration();
+    conf.set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, 
CustomObserver.class.getName());
+    conf.setStrings(
+      CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
+      MasterSyncObserver.class.getName(), CPMasterObserver.class.getName());
     conf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 5);
     conf.setBoolean(QuotaUtil.QUOTA_CONF_KEY, true);
     conf.setClass("hbase.coprocessor.regionserver.classes", 
CPRegionServerObserver.class,
@@ -309,6 +308,33 @@ public class TestNamespaceAuditor {
     }
   }
 
+  public static class CPMasterObserver extends BaseMasterObserver {
+    private volatile boolean shouldFailMerge = false;
+
+    public void failMerge(boolean fail) {
+      shouldFailMerge = fail;
+    }
+
+    private boolean triggered = false;
+
+    public synchronized void waitUtilTriggered() throws InterruptedException {
+      while (!triggered) {
+        wait();
+      }
+    }
+
+    @Override
+    public synchronized void preMergeRegionsAction(
+        final ObserverContext<MasterCoprocessorEnvironment> ctx,
+        final HRegionInfo[] regionsToMerge) throws IOException {
+      triggered = true;
+      notifyAll();
+      if (shouldFailMerge) {
+        throw new IOException("fail merge");
+      }
+    }
+  }
+
   @Test
   public void testRegionMerge() throws Exception {
     String nsp1 = prefix + "_regiontest";
@@ -414,18 +440,17 @@ public class TestNamespaceAuditor {
 
     // fail region merge through Coprocessor hook
     MiniHBaseCluster cluster = UTIL.getHBaseCluster();
-    HRegionServer regionServer = cluster.getRegionServer(0);
-    RegionServerCoprocessorHost cpHost = 
regionServer.getRegionServerCoprocessorHost();
-    Coprocessor coprocessor = 
cpHost.findCoprocessor(CPRegionServerObserver.class.getName());
-    CPRegionServerObserver regionServerObserver = (CPRegionServerObserver) 
coprocessor;
-    regionServerObserver.failMerge(true);
-    regionServerObserver.triggered = false;
+    MasterCoprocessorHost cpHost = 
cluster.getMaster().getMasterCoprocessorHost();
+    Coprocessor coprocessor = 
cpHost.findCoprocessor(CPMasterObserver.class.getName());
+    CPMasterObserver masterObserver = (CPMasterObserver) coprocessor;
+    masterObserver.failMerge(true);
+    masterObserver.triggered = false;
 
     ADMIN.mergeRegionsAsync(
       hris.get(1).getEncodedNameAsBytes(),
       hris.get(2).getEncodedNameAsBytes(),
       false);
-    regionServerObserver.waitUtilTriggered();
+    masterObserver.waitUtilTriggered();
     hris = ADMIN.getTableRegions(tableTwo);
     assertEquals(initialRegions, hris.size());
     Collections.sort(hris);

http://git-wip-us.apache.org/repos/asf/hbase/blob/0a240778/hbase-server/src/test/java/org/apache/hadoop/hbase/replication/TestSerialReplication.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/replication/TestSerialReplication.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/replication/TestSerialReplication.java
index 2cb4ecc..6fcccaf 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/replication/TestSerialReplication.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/replication/TestSerialReplication.java
@@ -55,12 +55,10 @@ import 
org.apache.hadoop.hbase.zookeeper.MiniZooKeeperCluster;
 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 
 @Category({ ReplicationTests.class, LargeTests.class })
 public class TestSerialReplication {
@@ -270,7 +268,7 @@ public class TestSerialReplication {
       }
       List<Pair<HRegionInfo, ServerName>> regions =
           
MetaTableAccessor.getTableRegionsAndLocations(utility1.getConnection(), 
tableName);
-      
utility1.getHBaseAdmin().mergeRegions(regions.get(0).getFirst().getRegionName(),
+      
utility1.getHBaseAdmin().mergeRegionsAsync(regions.get(0).getFirst().getRegionName(),
           regions.get(1).getFirst().getRegionName(), true);
       waitTableHasRightNumberOfRegions(tableName, 1);
       for (int i = 11; i < 100; i += 10) {

Reply via email to