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;
019
020import static org.junit.jupiter.api.Assertions.assertEquals;
021import static org.junit.jupiter.api.Assertions.assertNotNull;
022import static org.junit.jupiter.api.Assertions.assertTrue;
023
024import java.util.List;
025import java.util.concurrent.TimeUnit;
026import org.apache.hadoop.hbase.Waiter.ExplainingPredicate;
027import org.apache.hadoop.hbase.client.AsyncConnection;
028import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
029import org.apache.hadoop.hbase.client.ConnectionFactory;
030import org.apache.hadoop.hbase.client.RegionInfo;
031import org.apache.hadoop.hbase.client.TableDescriptor;
032import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
033import org.apache.hadoop.hbase.testclassification.MediumTests;
034import org.apache.hadoop.hbase.testclassification.MiscTests;
035import org.apache.hadoop.hbase.util.Bytes;
036import org.junit.jupiter.api.AfterAll;
037import org.junit.jupiter.api.BeforeAll;
038import org.junit.jupiter.api.Tag;
039import org.junit.jupiter.api.Test;
040
041@Tag(MiscTests.TAG)
042@Tag(MediumTests.TAG)
043public class TestSplitMerge {
044
045  private static final HBaseTestingUtil UTIL = new HBaseTestingUtil();
046
047  @BeforeAll
048  public static void setUp() throws Exception {
049    UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_META_OPERATION_TIMEOUT, 1000);
050    UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 2);
051    UTIL.startMiniCluster(1);
052  }
053
054  @AfterAll
055  public static void tearDown() throws Exception {
056    UTIL.shutdownMiniCluster();
057  }
058
059  @Test
060  public void test() throws Exception {
061    TableName tableName = TableName.valueOf("SplitMerge");
062    byte[] family = Bytes.toBytes("CF");
063    TableDescriptor td = TableDescriptorBuilder.newBuilder(tableName)
064      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(family)).build();
065    UTIL.getAdmin().createTable(td, new byte[][] { Bytes.toBytes(1) });
066    UTIL.waitTableAvailable(tableName);
067    UTIL.getAdmin().split(tableName, Bytes.toBytes(2));
068    UTIL.waitFor(30000, new ExplainingPredicate<Exception>() {
069
070      @Override
071      public boolean evaluate() throws Exception {
072        return UTIL.getMiniHBaseCluster().getRegions(tableName).size() == 3;
073      }
074
075      @Override
076      public String explainFailure() throws Exception {
077        return "Split has not finished yet";
078      }
079    });
080    UTIL.waitUntilNoRegionsInTransition();
081    RegionInfo regionA = null;
082    RegionInfo regionB = null;
083    for (RegionInfo region : UTIL.getAdmin().getRegions(tableName)) {
084      if (region.getStartKey().length == 0) {
085        regionA = region;
086      } else if (Bytes.equals(region.getStartKey(), Bytes.toBytes(1))) {
087        regionB = region;
088      }
089    }
090    assertNotNull(regionA);
091    assertNotNull(regionB);
092    UTIL.getAdmin().mergeRegionsAsync(regionA.getRegionName(), regionB.getRegionName(), false)
093      .get(30, TimeUnit.SECONDS);
094    assertEquals(2, UTIL.getAdmin().getRegions(tableName).size());
095
096    ServerName expected = UTIL.getMiniHBaseCluster().getRegionServer(0).getServerName();
097    assertEquals(expected, UTIL.getConnection().getRegionLocator(tableName)
098      .getRegionLocation(Bytes.toBytes(1), true).getServerName());
099    try (AsyncConnection asyncConn =
100      ConnectionFactory.createAsyncConnection(UTIL.getConfiguration()).get()) {
101      assertEquals(expected, asyncConn.getRegionLocator(tableName)
102        .getRegionLocation(Bytes.toBytes(1), true).get().getServerName());
103    }
104  }
105
106  @Test
107  public void testMergeRegionOrder() throws Exception {
108    int regionCount = 20;
109
110    TableName tableName = TableName.valueOf("MergeRegionOrder");
111    byte[] family = Bytes.toBytes("CF");
112    TableDescriptor td = TableDescriptorBuilder.newBuilder(tableName)
113      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(family)).build();
114
115    byte[][] splitKeys = new byte[regionCount - 1][];
116
117    for (int c = 0; c < regionCount - 1; c++) {
118      splitKeys[c] = Bytes.toBytes(c + 1 * 1000);
119    }
120
121    UTIL.getAdmin().createTable(td, splitKeys);
122    UTIL.waitTableAvailable(tableName);
123
124    List<RegionInfo> regions = UTIL.getAdmin().getRegions(tableName);
125
126    byte[][] regionNames = new byte[regionCount][];
127    for (int c = 0; c < regionCount; c++) {
128      regionNames[c] = regions.get(c).getRegionName();
129    }
130
131    UTIL.getAdmin().mergeRegionsAsync(regionNames, false).get(60, TimeUnit.SECONDS);
132
133    List<RegionInfo> mergedRegions =
134      MetaTableAccessor.getTableRegions(UTIL.getConnection(), tableName);
135
136    assertEquals(1, mergedRegions.size());
137
138    RegionInfo mergedRegion = mergedRegions.get(0);
139
140    List<RegionInfo> mergeParentRegions = UTIL.getMiniHBaseCluster().getMaster()
141      .getAssignmentManager().getRegionStateStore().getMergeRegions(mergedRegion);
142
143    assertEquals(mergeParentRegions.size(), regionCount);
144
145    for (int c = 0; c < regionCount - 1; c++) {
146      assertTrue(Bytes.compareTo(mergeParentRegions.get(c).getStartKey(),
147        mergeParentRegions.get(c + 1).getStartKey()) < 0);
148    }
149  }
150}