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.HBaseTestingUtility;
033import org.apache.hadoop.hbase.HRegionInfo;
034import org.apache.hadoop.hbase.ServerName;
035import org.apache.hadoop.hbase.TableName;
036import org.apache.hadoop.hbase.master.assignment.AssignmentTestingUtil;
037import org.apache.hadoop.hbase.master.assignment.SplitTableRegionProcedure;
038import org.apache.hadoop.hbase.master.procedure.DeleteTableProcedure;
039import org.apache.hadoop.hbase.master.procedure.DisableTableProcedure;
040import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
041import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
042import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
043import org.apache.hadoop.hbase.testclassification.ClientTests;
044import org.apache.hadoop.hbase.testclassification.MediumTests;
045import org.apache.hadoop.hbase.util.Bytes;
046import org.apache.hadoop.hbase.util.Threads;
047import org.junit.After;
048import org.junit.Before;
049import org.junit.ClassRule;
050import org.junit.Ignore;
051import org.junit.Rule;
052import org.junit.Test;
053import org.junit.experimental.categories.Category;
054import org.junit.rules.TestName;
055
056@Category({ MediumTests.class, ClientTests.class })
057public class TestSplitOrMergeStatus {
058
059  @ClassRule
060  public static final HBaseClassTestRule CLASS_RULE =
061    HBaseClassTestRule.forClass(TestSplitOrMergeStatus.class);
062
063  private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
064  private static byte[] FAMILY = Bytes.toBytes("testFamily");
065
066  @Rule
067  public TestName name = new TestName();
068
069  /**
070   * @throws java.lang.Exception
071   */
072  @Before
073  public void setUp() throws Exception {
074    TEST_UTIL.startMiniCluster(2);
075  }
076
077  /**
078   * @throws java.lang.Exception
079   */
080  @After
081  public void tearDown() throws Exception {
082    TEST_UTIL.shutdownMiniCluster();
083  }
084
085  @Test
086  public void testSplitSwitch() throws Exception {
087    final TableName tableName = TableName.valueOf(name.getMethodName());
088    Table t = TEST_UTIL.createTable(tableName, FAMILY);
089    TEST_UTIL.loadTable(t, FAMILY, false);
090
091    RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(t.getName());
092    int originalCount = locator.getAllRegionLocations().size();
093
094    Admin admin = TEST_UTIL.getAdmin();
095    initSwitchStatus(admin);
096    assertTrue(admin.splitSwitch(false, false));
097    try {
098      admin.split(t.getName());
099      fail("Shouldn't get here");
100    } catch (DoNotRetryIOException dnioe) {
101      // Expected
102    }
103    int count = admin.getTableRegions(tableName).size();
104    assertTrue(originalCount == count);
105    assertFalse(admin.splitSwitch(true, false));
106    admin.split(t.getName());
107    while ((count = admin.getTableRegions(tableName).size()) == originalCount) {
108      Threads.sleep(1);
109      ;
110    }
111    count = admin.getTableRegions(tableName).size();
112    assertTrue(originalCount < count);
113    admin.close();
114  }
115
116  @Ignore
117  @Test
118  public void testMergeSwitch() throws Exception {
119    final TableName tableName = TableName.valueOf(name.getMethodName());
120    Table t = TEST_UTIL.createTable(tableName, FAMILY);
121    TEST_UTIL.loadTable(t, FAMILY, false);
122
123    Admin admin = TEST_UTIL.getAdmin();
124    int originalCount = admin.getTableRegions(tableName).size();
125    initSwitchStatus(admin);
126    admin.split(t.getName());
127    int postSplitCount = -1;
128    while ((postSplitCount = admin.getTableRegions(tableName).size()) == originalCount) {
129      Threads.sleep(1);
130      ;
131    }
132    assertTrue("originalCount=" + originalCount + ", newCount=" + postSplitCount,
133      originalCount != postSplitCount);
134
135    // Merge switch is off so merge should NOT succeed.
136    boolean[] results = admin.setSplitOrMergeEnabled(false, false, MasterSwitchType.MERGE);
137    assertEquals(1, results.length);
138    assertTrue(results[0]);
139    List<HRegionInfo> regions = admin.getTableRegions(t.getName());
140    assertTrue(regions.size() > 1);
141    Future<?> f = admin.mergeRegionsAsync(regions.get(0).getEncodedNameAsBytes(),
142      regions.get(1).getEncodedNameAsBytes(), true);
143    try {
144      f.get(10, TimeUnit.SECONDS);
145      fail("Should not get here.");
146    } catch (ExecutionException ee) {
147      // Expected.
148    }
149    int count = admin.getTableRegions(tableName).size();
150    assertTrue("newCount=" + postSplitCount + ", count=" + count, postSplitCount == count);
151
152    results = admin.setSplitOrMergeEnabled(true, false, MasterSwitchType.MERGE);
153    regions = admin.getTableRegions(t.getName());
154    assertEquals(1, results.length);
155    assertFalse(results[0]);
156    f = admin.mergeRegionsAsync(regions.get(0).getEncodedNameAsBytes(),
157      regions.get(1).getEncodedNameAsBytes(), true);
158    f.get(10, TimeUnit.SECONDS);
159    count = admin.getTableRegions(tableName).size();
160    assertTrue((postSplitCount / 2 /* Merge */) == count);
161    admin.close();
162  }
163
164  @Test
165  public void testMultiSwitches() throws IOException {
166    Admin admin = TEST_UTIL.getAdmin();
167    boolean[] switches =
168      admin.setSplitOrMergeEnabled(false, false, MasterSwitchType.SPLIT, MasterSwitchType.MERGE);
169    for (boolean s : switches) {
170      assertTrue(s);
171    }
172    assertFalse(admin.isSplitOrMergeEnabled(MasterSwitchType.SPLIT));
173    assertFalse(admin.isSplitOrMergeEnabled(MasterSwitchType.MERGE));
174    admin.close();
175  }
176
177  @Test
178  public void testSplitRegionReplicaRitRecovery() throws Exception {
179    int startRowNum = 11;
180    int rowCount = 60;
181    final TableName tableName = TableName.valueOf(name.getMethodName());
182    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
183    TEST_UTIL.getAdmin().createTable(TableDescriptorBuilder.newBuilder(tableName)
184      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY)).setRegionReplication(2).build());
185    TEST_UTIL.waitUntilAllRegionsAssigned(tableName);
186    ServerName serverName =
187      RegionReplicaTestHelper.getRSCarryingReplica(TEST_UTIL, tableName, 1).get();
188    List<RegionInfo> regions = TEST_UTIL.getAdmin().getRegions(tableName);
189    insertData(tableName, startRowNum, rowCount);
190    int splitRowNum = startRowNum + rowCount / 2;
191    byte[] splitKey = Bytes.toBytes("" + splitRowNum);
192    // Split region of the table
193    long procId = procExec.submitProcedure(
194      new SplitTableRegionProcedure(procExec.getEnvironment(), regions.get(0), splitKey));
195    // Wait the completion
196    ProcedureTestingUtility.waitProcedure(procExec, procId);
197    // Disable the table
198    long procId1 = procExec
199      .submitProcedure(new DisableTableProcedure(procExec.getEnvironment(), tableName, false));
200    // Wait the completion
201    ProcedureTestingUtility.waitProcedure(procExec, procId1);
202    // Delete Table
203    long procId2 =
204      procExec.submitProcedure(new DeleteTableProcedure(procExec.getEnvironment(), tableName));
205    // Wait the completion
206    ProcedureTestingUtility.waitProcedure(procExec, procId2);
207    AssignmentTestingUtil.killRs(TEST_UTIL, serverName);
208    Threads.sleepWithoutInterrupt(5000);
209    boolean hasRegionsInTransition = TEST_UTIL.getMiniHBaseCluster().getMaster()
210      .getAssignmentManager().getRegionStates().hasRegionsInTransition();
211    assertEquals(false, hasRegionsInTransition);
212  }
213
214  private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
215    return TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor();
216  }
217
218  private void insertData(final TableName tableName, int startRow, int rowCount)
219    throws IOException {
220    Table t = TEST_UTIL.getConnection().getTable(tableName);
221    Put p;
222    for (int i = 0; i < rowCount; i++) {
223      p = new Put(Bytes.toBytes("" + (startRow + i)));
224      p.addColumn(FAMILY, Bytes.toBytes("q1"), Bytes.toBytes(i));
225      t.put(p);
226    }
227  }
228
229  private void initSwitchStatus(Admin admin) throws IOException {
230    if (!admin.isSplitEnabled()) {
231      admin.splitSwitch(true, false);
232    }
233    if (!admin.isMergeEnabled()) {
234      admin.mergeSwitch(true, false);
235    }
236    assertTrue(admin.isSplitEnabled());
237    assertTrue(admin.isMergeEnabled());
238  }
239}