001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.client;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertTrue;
023import static org.junit.Assert.fail;
024
025import java.io.IOException;
026import java.util.List;
027import java.util.concurrent.ExecutionException;
028import java.util.concurrent.Future;
029import java.util.concurrent.TimeUnit;
030import org.apache.hadoop.hbase.DoNotRetryIOException;
031import org.apache.hadoop.hbase.HBaseClassTestRule;
032import org.apache.hadoop.hbase.HBaseTestingUtil;
033import org.apache.hadoop.hbase.ServerName;
034import org.apache.hadoop.hbase.TableName;
035import org.apache.hadoop.hbase.master.assignment.AssignmentTestingUtil;
036import org.apache.hadoop.hbase.master.assignment.SplitTableRegionProcedure;
037import org.apache.hadoop.hbase.master.procedure.DeleteTableProcedure;
038import org.apache.hadoop.hbase.master.procedure.DisableTableProcedure;
039import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
040import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
041import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
042import org.apache.hadoop.hbase.testclassification.ClientTests;
043import org.apache.hadoop.hbase.testclassification.MediumTests;
044import org.apache.hadoop.hbase.util.Bytes;
045import org.apache.hadoop.hbase.util.Threads;
046import org.junit.After;
047import org.junit.Before;
048import org.junit.ClassRule;
049import org.junit.Ignore;
050import org.junit.Rule;
051import org.junit.Test;
052import org.junit.experimental.categories.Category;
053import org.junit.rules.TestName;
054
055@Category({ MediumTests.class, ClientTests.class })
056public class TestSplitOrMergeStatus {
057
058  @ClassRule
059  public static final HBaseClassTestRule CLASS_RULE =
060    HBaseClassTestRule.forClass(TestSplitOrMergeStatus.class);
061
062  private final static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
063  private static byte[] FAMILY = Bytes.toBytes("testFamily");
064
065  @Rule
066  public TestName name = new TestName();
067
068  @Before
069  public void setUp() throws Exception {
070    TEST_UTIL.startMiniCluster(2);
071  }
072
073  @After
074  public void tearDown() throws Exception {
075    TEST_UTIL.shutdownMiniCluster();
076  }
077
078  @Test
079  public void testSplitSwitch() throws Exception {
080    final TableName tableName = TableName.valueOf(name.getMethodName());
081    Table t = TEST_UTIL.createTable(tableName, FAMILY);
082    TEST_UTIL.loadTable(t, FAMILY, false);
083
084    RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(t.getName());
085    int originalCount = locator.getAllRegionLocations().size();
086
087    Admin admin = TEST_UTIL.getAdmin();
088    initSwitchStatus(admin);
089    assertTrue(admin.splitSwitch(false, false));
090    try {
091      admin.split(t.getName());
092      fail("Shouldn't get here");
093    } catch (DoNotRetryIOException dnioe) {
094      // Expected
095    }
096    int count = admin.getRegions(tableName).size();
097    assertTrue(originalCount == count);
098    assertFalse(admin.splitSwitch(true, false));
099    admin.split(t.getName());
100    while ((count = admin.getRegions(tableName).size()) == originalCount) {
101      Threads.sleep(1);
102    }
103    count = admin.getRegions(tableName).size();
104    assertTrue(originalCount < count);
105    admin.close();
106  }
107
108  @Ignore
109  @Test
110  public void testMergeSwitch() throws Exception {
111    final TableName tableName = TableName.valueOf(name.getMethodName());
112    Table t = TEST_UTIL.createTable(tableName, FAMILY);
113    TEST_UTIL.loadTable(t, FAMILY, false);
114
115    Admin admin = TEST_UTIL.getAdmin();
116    int originalCount = admin.getRegions(tableName).size();
117    initSwitchStatus(admin);
118    admin.split(t.getName());
119    int postSplitCount = -1;
120    while ((postSplitCount = admin.getRegions(tableName).size()) == originalCount) {
121      Threads.sleep(1);
122    }
123    assertTrue("originalCount=" + originalCount + ", newCount=" + postSplitCount,
124      originalCount != postSplitCount);
125
126    // Merge switch is off so merge should NOT succeed.
127    boolean result = admin.mergeSwitch(false, false);
128    assertTrue(result);
129    List<RegionInfo> regions = admin.getRegions(t.getName());
130    assertTrue(regions.size() > 1);
131    Future<?> f = admin.mergeRegionsAsync(regions.get(0).getEncodedNameAsBytes(),
132      regions.get(1).getEncodedNameAsBytes(), true);
133    try {
134      f.get(10, TimeUnit.SECONDS);
135      fail("Should not get here.");
136    } catch (ExecutionException ee) {
137      // Expected.
138    }
139    int count = admin.getRegions(tableName).size();
140    assertTrue("newCount=" + postSplitCount + ", count=" + count, postSplitCount == count);
141
142    result = admin.mergeSwitch(true, false);
143    regions = admin.getRegions(t.getName());
144    assertFalse(result);
145    f = admin.mergeRegionsAsync(regions.get(0).getEncodedNameAsBytes(),
146      regions.get(1).getEncodedNameAsBytes(), true);
147    f.get(10, TimeUnit.SECONDS);
148    count = admin.getRegions(tableName).size();
149    assertTrue((postSplitCount / 2 /* Merge */) == count);
150    admin.close();
151  }
152
153  @Test
154  public void testMultiSwitches() throws IOException {
155    Admin admin = TEST_UTIL.getAdmin();
156    assertTrue(admin.splitSwitch(false, false));
157    assertTrue(admin.mergeSwitch(false, false));
158
159    assertFalse(admin.isSplitEnabled());
160    assertFalse(admin.isMergeEnabled());
161    admin.close();
162  }
163
164  @Test
165  public void testSplitRegionReplicaRitRecovery() throws Exception {
166    int startRowNum = 11;
167    int rowCount = 60;
168    final TableName tableName = TableName.valueOf(name.getMethodName());
169    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
170    TEST_UTIL.getAdmin().createTable(TableDescriptorBuilder.newBuilder(tableName)
171      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY)).setRegionReplication(2).build());
172    TEST_UTIL.waitUntilAllRegionsAssigned(tableName);
173    ServerName serverName =
174      RegionReplicaTestHelper.getRSCarryingReplica(TEST_UTIL, tableName, 1).get();
175    List<RegionInfo> regions = TEST_UTIL.getAdmin().getRegions(tableName);
176    insertData(tableName, startRowNum, rowCount);
177    int splitRowNum = startRowNum + rowCount / 2;
178    byte[] splitKey = Bytes.toBytes("" + splitRowNum);
179    // Split region of the table
180    long procId = procExec.submitProcedure(
181      new SplitTableRegionProcedure(procExec.getEnvironment(), regions.get(0), splitKey));
182    // Wait the completion
183    ProcedureTestingUtility.waitProcedure(procExec, procId);
184    // Disable the table
185    long procId1 = procExec
186      .submitProcedure(new DisableTableProcedure(procExec.getEnvironment(), tableName, false));
187    // Wait the completion
188    ProcedureTestingUtility.waitProcedure(procExec, procId1);
189    // Delete Table
190    long procId2 =
191      procExec.submitProcedure(new DeleteTableProcedure(procExec.getEnvironment(), tableName));
192    // Wait the completion
193    ProcedureTestingUtility.waitProcedure(procExec, procId2);
194    AssignmentTestingUtil.killRs(TEST_UTIL, serverName);
195    Threads.sleepWithoutInterrupt(5000);
196    boolean hasRegionsInTransition = TEST_UTIL.getMiniHBaseCluster().getMaster()
197      .getAssignmentManager().getRegionStates().hasRegionsInTransition();
198    assertEquals(false, hasRegionsInTransition);
199  }
200
201  private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
202    return TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor();
203  }
204
205  private void insertData(final TableName tableName, int startRow, int rowCount)
206    throws IOException {
207    Table t = TEST_UTIL.getConnection().getTable(tableName);
208    Put p;
209    for (int i = 0; i < rowCount; i++) {
210      p = new Put(Bytes.toBytes("" + (startRow + i)));
211      p.addColumn(FAMILY, Bytes.toBytes("q1"), Bytes.toBytes(i));
212      t.put(p);
213    }
214  }
215
216  private void initSwitchStatus(Admin admin) throws IOException {
217    if (!admin.isSplitEnabled()) {
218      admin.splitSwitch(true, false);
219    }
220    if (!admin.isMergeEnabled()) {
221      admin.mergeSwitch(true, false);
222    }
223    assertTrue(admin.isSplitEnabled());
224    assertTrue(admin.isMergeEnabled());
225  }
226}