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