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.apache.hadoop.hbase.TableName.META_TABLE_NAME;
021import static org.hamcrest.CoreMatchers.instanceOf;
022import static org.junit.Assert.assertEquals;
023import static org.junit.Assert.assertFalse;
024import static org.junit.Assert.assertThat;
025import static org.junit.Assert.assertTrue;
026import static org.junit.Assert.fail;
027
028import java.util.ArrayList;
029import java.util.Collections;
030import java.util.List;
031import java.util.concurrent.ExecutionException;
032import java.util.stream.Collectors;
033import org.apache.hadoop.hbase.AsyncMetaTableAccessor;
034import org.apache.hadoop.hbase.HBaseClassTestRule;
035import org.apache.hadoop.hbase.HConstants;
036import org.apache.hadoop.hbase.HRegionLocation;
037import org.apache.hadoop.hbase.TableName;
038import org.apache.hadoop.hbase.testclassification.ClientTests;
039import org.apache.hadoop.hbase.testclassification.LargeTests;
040import org.apache.hadoop.hbase.util.Bytes;
041import org.apache.hadoop.hbase.util.Threads;
042import org.junit.ClassRule;
043import org.junit.Ignore;
044import org.junit.Test;
045import org.junit.experimental.categories.Category;
046import org.junit.runner.RunWith;
047import org.junit.runners.Parameterized;
048
049/**
050 * Class to test asynchronous region admin operations.
051 * @see TestAsyncRegionAdminApi This test and it used to be joined it was taking longer than our
052 * ten minute timeout so they were split.
053 */
054@RunWith(Parameterized.class)
055@Category({ LargeTests.class, ClientTests.class })
056public class TestAsyncRegionAdminApi2 extends TestAsyncAdminBase {
057
058  @ClassRule
059  public static final HBaseClassTestRule CLASS_RULE =
060      HBaseClassTestRule.forClass(TestAsyncRegionAdminApi2.class);
061
062  @Test
063  public void testGetRegionLocation() throws Exception {
064    RawAsyncHBaseAdmin rawAdmin = (RawAsyncHBaseAdmin) ASYNC_CONN.getAdmin();
065    TEST_UTIL.createMultiRegionTable(tableName, HConstants.CATALOG_FAMILY);
066    AsyncTableRegionLocator locator = ASYNC_CONN.getRegionLocator(tableName);
067    HRegionLocation regionLocation = locator.getRegionLocation(Bytes.toBytes("mmm")).get();
068    RegionInfo region = regionLocation.getRegion();
069    byte[] regionName = regionLocation.getRegion().getRegionName();
070    HRegionLocation location = rawAdmin.getRegionLocation(regionName).get();
071    assertTrue(Bytes.equals(regionName, location.getRegion().getRegionName()));
072    location = rawAdmin.getRegionLocation(region.getEncodedNameAsBytes()).get();
073    assertTrue(Bytes.equals(regionName, location.getRegion().getRegionName()));
074  }
075
076  @Test
077  public void testSplitSwitch() throws Exception {
078    createTableWithDefaultConf(tableName);
079    byte[][] families = {FAMILY};
080    final int rows = 10000;
081    TestAsyncRegionAdminApi.loadData(tableName, families, rows);
082
083    AsyncTable<AdvancedScanResultConsumer> metaTable = ASYNC_CONN.getTable(META_TABLE_NAME);
084    List<HRegionLocation> regionLocations =
085        AsyncMetaTableAccessor.getTableHRegionLocations(metaTable, tableName).get();
086    int originalCount = regionLocations.size();
087
088    initSplitMergeSwitch();
089    assertTrue(admin.splitSwitch(false).get());
090    try {
091      admin.split(tableName, Bytes.toBytes(rows / 2)).join();
092    } catch (Exception e) {
093      //Expected
094    }
095    int count = admin.getRegions(tableName).get().size();
096    assertTrue(originalCount == count);
097
098    assertFalse(admin.splitSwitch(true).get());
099    admin.split(tableName).join();
100    while ((count = admin.getRegions(tableName).get().size()) == originalCount) {
101      Threads.sleep(100);
102    }
103    assertTrue(originalCount < count);
104  }
105
106  @Test
107  @Ignore
108  // It was ignored in TestSplitOrMergeStatus, too
109  public void testMergeSwitch() throws Exception {
110    createTableWithDefaultConf(tableName);
111    byte[][] families = {FAMILY};
112    TestAsyncRegionAdminApi.loadData(tableName, families, 1000);
113
114    AsyncTable<AdvancedScanResultConsumer> metaTable = ASYNC_CONN.getTable(META_TABLE_NAME);
115    List<HRegionLocation> regionLocations =
116        AsyncMetaTableAccessor.getTableHRegionLocations(metaTable, tableName).get();
117    int originalCount = regionLocations.size();
118
119    initSplitMergeSwitch();
120    admin.split(tableName).join();
121    int postSplitCount = originalCount;
122    while ((postSplitCount = admin.getRegions(tableName).get().size()) == originalCount) {
123      Threads.sleep(100);
124    }
125    assertTrue("originalCount=" + originalCount + ", postSplitCount=" + postSplitCount,
126        originalCount != postSplitCount);
127
128    // Merge switch is off so merge should NOT succeed.
129    assertTrue(admin.mergeSwitch(false).get());
130    List<RegionInfo> regions = admin.getRegions(tableName).get();
131    assertTrue(regions.size() > 1);
132    admin.mergeRegions(regions.get(0).getRegionName(), regions.get(1).getRegionName(), true).join();
133    int count = admin.getRegions(tableName).get().size();
134    assertTrue("postSplitCount=" + postSplitCount + ", count=" + count, postSplitCount == count);
135
136    // Merge switch is on so merge should succeed.
137    assertFalse(admin.mergeSwitch(true).get());
138    admin.mergeRegions(regions.get(0).getRegionName(), regions.get(1).getRegionName(), true).join();
139    count = admin.getRegions(tableName).get().size();
140    assertTrue((postSplitCount / 2) == count);
141  }
142
143  private void initSplitMergeSwitch() throws Exception {
144    if (!admin.isSplitEnabled().get()) {
145      admin.splitSwitch(true).get();
146    }
147    if (!admin.isMergeEnabled().get()) {
148      admin.mergeSwitch(true).get();
149    }
150    assertTrue(admin.isSplitEnabled().get());
151    assertTrue(admin.isMergeEnabled().get());
152  }
153
154  @Test
155  public void testMergeRegions() throws Exception {
156    byte[][] splitRows = new byte[][]{Bytes.toBytes("3"), Bytes.toBytes("6")};
157    createTableWithDefaultConf(tableName, splitRows);
158
159    AsyncTable<AdvancedScanResultConsumer> metaTable = ASYNC_CONN.getTable(META_TABLE_NAME);
160    List<HRegionLocation> regionLocations = AsyncMetaTableAccessor
161      .getTableHRegionLocations(metaTable, tableName).get();
162    RegionInfo regionA;
163    RegionInfo regionB;
164
165    // merge with full name
166    assertEquals(3, regionLocations.size());
167    regionA = regionLocations.get(0).getRegion();
168    regionB = regionLocations.get(1).getRegion();
169    admin.mergeRegions(regionA.getRegionName(), regionB.getRegionName(), false).get();
170
171    regionLocations = AsyncMetaTableAccessor
172      .getTableHRegionLocations(metaTable, tableName).get();
173    assertEquals(2, regionLocations.size());
174    // merge with encoded name
175    regionA = regionLocations.get(0).getRegion();
176    regionB = regionLocations.get(1).getRegion();
177    admin.mergeRegions(regionA.getRegionName(), regionB.getRegionName(), false).get();
178
179    regionLocations = AsyncMetaTableAccessor
180      .getTableHRegionLocations(metaTable, tableName).get();
181    assertEquals(1, regionLocations.size());
182  }
183
184  @Test
185  public void testMergeRegionsInvalidRegionCount() throws Exception {
186    byte[][] splitRows = new byte[][] { Bytes.toBytes("3"), Bytes.toBytes("6") };
187    createTableWithDefaultConf(tableName, splitRows);
188    List<RegionInfo> regions = admin.getRegions(tableName).join();
189    // 0
190    try {
191      admin.mergeRegions(Collections.emptyList(), false).get();
192      fail();
193    } catch (ExecutionException e) {
194      // expected
195      assertThat(e.getCause(), instanceOf(IllegalArgumentException.class));
196    }
197    // 1
198    try {
199      admin.mergeRegions(regions.stream().limit(1).map(RegionInfo::getEncodedNameAsBytes)
200        .collect(Collectors.toList()), false).get();
201      fail();
202    } catch (ExecutionException e) {
203      // expected
204      assertThat(e.getCause(), instanceOf(IllegalArgumentException.class));
205    }
206  }
207
208  @Test
209  public void testSplitTable() throws Exception {
210    initSplitMergeSwitch();
211    splitTest(TableName.valueOf("testSplitTable"), 3000, false, null);
212    splitTest(TableName.valueOf("testSplitTableWithSplitPoint"), 3000, false, Bytes.toBytes("3"));
213    splitTest(TableName.valueOf("testSplitTableRegion"), 3000, true, null);
214    splitTest(TableName.valueOf("testSplitTableRegionWithSplitPoint2"), 3000, true, Bytes.toBytes("3"));
215  }
216
217  private void
218  splitTest(TableName tableName, int rowCount, boolean isSplitRegion, byte[] splitPoint)
219      throws Exception {
220    // create table
221    createTableWithDefaultConf(tableName);
222
223    AsyncTable<AdvancedScanResultConsumer> metaTable = ASYNC_CONN.getTable(META_TABLE_NAME);
224    List<HRegionLocation> regionLocations = AsyncMetaTableAccessor
225      .getTableHRegionLocations(metaTable, tableName).get();
226    assertEquals(1, regionLocations.size());
227
228    AsyncTable<?> table = ASYNC_CONN.getTable(tableName);
229    List<Put> puts = new ArrayList<>();
230    for (int i = 0; i < rowCount; i++) {
231      Put put = new Put(Bytes.toBytes(i));
232      put.addColumn(FAMILY, null, Bytes.toBytes("value" + i));
233      puts.add(put);
234    }
235    table.putAll(puts).join();
236
237    if (isSplitRegion) {
238      if (splitPoint == null) {
239        admin.splitRegion(regionLocations.get(0).getRegion().getRegionName()).get();
240      } else {
241        admin.splitRegion(regionLocations.get(0).getRegion().getRegionName(), splitPoint).get();
242      }
243    } else {
244      if (splitPoint == null) {
245        admin.split(tableName).get();
246      } else {
247        admin.split(tableName, splitPoint).get();
248      }
249    }
250
251    int count = 0;
252    for (int i = 0; i < 45; i++) {
253      try {
254        regionLocations = AsyncMetaTableAccessor
255          .getTableHRegionLocations(metaTable, tableName).get();
256        count = regionLocations.size();
257        if (count >= 2) {
258          break;
259        }
260        Thread.sleep(1000L);
261      } catch (Exception e) {
262        LOG.error(e.toString(), e);
263      }
264    }
265    assertEquals(2, count);
266  }
267}