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;
030
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    boolean[] results = admin.setSplitOrMergeEnabled(false, false, MasterSwitchType.SPLIT);
097    assertEquals(1, results.length);
098    assertTrue(results[0]);
099    admin.split(t.getName());
100    int count = admin.getTableRegions(tableName).size();
101    assertTrue(originalCount == count);
102    results = admin.setSplitOrMergeEnabled(true, false, MasterSwitchType.SPLIT);
103    assertEquals(1, results.length);
104    assertFalse(results[0]);
105    admin.split(t.getName());
106    while ((count = admin.getTableRegions(tableName).size()) == originalCount) {
107      Threads.sleep(1);;
108    }
109    count = admin.getTableRegions(tableName).size();
110    assertTrue(originalCount < count);
111    admin.close();
112  }
113
114
115  @Ignore @Test
116  public void testMergeSwitch() throws Exception {
117    final TableName tableName = TableName.valueOf(name.getMethodName());
118    Table t = TEST_UTIL.createTable(tableName, FAMILY);
119    TEST_UTIL.loadTable(t, FAMILY, false);
120
121    Admin admin = TEST_UTIL.getAdmin();
122    int originalCount = admin.getTableRegions(tableName).size();
123    initSwitchStatus(admin);
124    admin.split(t.getName());
125    int postSplitCount = -1;
126    while ((postSplitCount = admin.getTableRegions(tableName).size()) == originalCount) {
127      Threads.sleep(1);;
128    }
129    assertTrue("originalCount=" + originalCount + ", newCount=" + postSplitCount,
130        originalCount != postSplitCount);
131
132    // Merge switch is off so merge should NOT succeed.
133    boolean[] results = admin.setSplitOrMergeEnabled(false, false, MasterSwitchType.MERGE);
134    assertEquals(1, results.length);
135    assertTrue(results[0]);
136    List<HRegionInfo> regions = admin.getTableRegions(t.getName());
137    assertTrue(regions.size() > 1);
138    Future<?> f = admin.mergeRegionsAsync(regions.get(0).getEncodedNameAsBytes(),
139      regions.get(1).getEncodedNameAsBytes(), true);
140    try {
141      f.get(10, TimeUnit.SECONDS);
142      fail("Should not get here.");
143    } catch (ExecutionException ee) {
144      // Expected.
145    }
146    int count = admin.getTableRegions(tableName).size();
147    assertTrue("newCount=" + postSplitCount + ", count=" + count, postSplitCount == count);
148
149    results = admin.setSplitOrMergeEnabled(true, false, MasterSwitchType.MERGE);
150    regions = admin.getTableRegions(t.getName());
151    assertEquals(1, results.length);
152    assertFalse(results[0]);
153    f = admin.mergeRegionsAsync(regions.get(0).getEncodedNameAsBytes(),
154      regions.get(1).getEncodedNameAsBytes(), true);
155    f.get(10, TimeUnit.SECONDS);
156    count = admin.getTableRegions(tableName).size();
157    assertTrue((postSplitCount / 2 /*Merge*/) == count);
158    admin.close();
159  }
160
161  @Test
162  public void testMultiSwitches() throws IOException {
163    Admin admin = TEST_UTIL.getAdmin();
164    boolean[] switches = admin.setSplitOrMergeEnabled(false, false,
165      MasterSwitchType.SPLIT, MasterSwitchType.MERGE);
166    for (boolean s : switches){
167      assertTrue(s);
168    }
169    assertFalse(admin.isSplitOrMergeEnabled(MasterSwitchType.SPLIT));
170    assertFalse(admin.isSplitOrMergeEnabled(MasterSwitchType.MERGE));
171    admin.close();
172  }
173
174  @Test
175  public void testSplitRegionReplicaRitRecovery() throws Exception {
176    int startRowNum = 11;
177    int rowCount = 60;
178    final TableName tableName = TableName.valueOf(name.getMethodName());
179    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
180    TEST_UTIL.getAdmin().createTable(TableDescriptorBuilder.newBuilder(tableName)
181        .setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY)).setRegionReplication(2).build());
182    TEST_UTIL.waitUntilAllRegionsAssigned(tableName);
183    ServerName serverName =
184        RegionReplicaTestHelper.getRSCarryingReplica(TEST_UTIL, tableName, 1).get();
185    List<RegionInfo> regions = TEST_UTIL.getAdmin().getRegions(tableName);
186    insertData(tableName, startRowNum, rowCount);
187    int splitRowNum = startRowNum + rowCount / 2;
188    byte[] splitKey = Bytes.toBytes("" + splitRowNum);
189    // Split region of the table
190    long procId = procExec.submitProcedure(
191      new SplitTableRegionProcedure(procExec.getEnvironment(), regions.get(0), splitKey));
192    // Wait the completion
193    ProcedureTestingUtility.waitProcedure(procExec, procId);
194    // Disable the table
195    long procId1 = procExec
196        .submitProcedure(new DisableTableProcedure(procExec.getEnvironment(), tableName, false));
197    // Wait the completion
198    ProcedureTestingUtility.waitProcedure(procExec, procId1);
199    // Delete Table
200    long procId2 =
201        procExec.submitProcedure(new DeleteTableProcedure(procExec.getEnvironment(), tableName));
202    // Wait the completion
203    ProcedureTestingUtility.waitProcedure(procExec, procId2);
204    AssignmentTestingUtil.killRs(TEST_UTIL, serverName);
205    Threads.sleepWithoutInterrupt(5000);
206    boolean hasRegionsInTransition = TEST_UTIL.getMiniHBaseCluster().getMaster()
207        .getAssignmentManager().getRegionStates().hasRegionsInTransition();
208    assertEquals(false, hasRegionsInTransition);
209  }
210
211  private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
212    return TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor();
213  }
214
215  private void insertData(final TableName tableName, int startRow, int rowCount)
216      throws IOException {
217    Table t = TEST_UTIL.getConnection().getTable(tableName);
218    Put p;
219    for (int i = 0; i < rowCount; i++) {
220      p = new Put(Bytes.toBytes("" + (startRow + i)));
221      p.addColumn(FAMILY, Bytes.toBytes("q1"), Bytes.toBytes(i));
222      t.put(p);
223    }
224  }
225
226  private void initSwitchStatus(Admin admin) throws IOException {
227    if (!admin.isSplitOrMergeEnabled(MasterSwitchType.SPLIT)) {
228      admin.setSplitOrMergeEnabled(true, false, MasterSwitchType.SPLIT);
229    }
230    if (!admin.isSplitOrMergeEnabled(MasterSwitchType.MERGE)) {
231      admin.setSplitOrMergeEnabled(true, false, MasterSwitchType.MERGE);
232    }
233    assertTrue(admin.isSplitOrMergeEnabled(MasterSwitchType.SPLIT));
234    assertTrue(admin.isSplitOrMergeEnabled(MasterSwitchType.MERGE));
235  }
236}