This is an automated email from the ASF dual-hosted git repository. huaxiangsun pushed a commit to branch branch-2 in repository https://gitbox.apache.org/repos/asf/hbase.git
The following commit(s) were added to refs/heads/branch-2 by this push: new b144d17 HBASE-24376 MergeNormalizer is merging non-adjacent regions and causing region overlaps/holes. (#1734) (#1758) b144d17 is described below commit b144d170442ad05ec1c87f04027ad0de94170995 Author: huaxiangsun <huaxiang...@apache.org> AuthorDate: Fri May 22 08:09:36 2020 -0700 HBASE-24376 MergeNormalizer is merging non-adjacent regions and causing region overlaps/holes. (#1734) (#1758) Signed-off-by: Viraj Jasani <vjas...@apache.org> Signed-off-by: Jan Hentschel <jan.hentsc...@ultratendency.com> Signed-off-by: Nick Dimiduk <ndimi...@apache.org> Signed-off-by: stack <st...@apache.org> --- .../normalizer/AbstractRegionNormalizer.java | 6 ++ .../master/normalizer/MergeNormalizationPlan.java | 5 +- .../TestSimpleRegionNormalizerOnCluster.java | 88 +++++++++++++++++++--- 3 files changed, 88 insertions(+), 11 deletions(-) diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/normalizer/AbstractRegionNormalizer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/normalizer/AbstractRegionNormalizer.java index 9a9f5f2..8b17f77 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/normalizer/AbstractRegionNormalizer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/normalizer/AbstractRegionNormalizer.java @@ -170,6 +170,12 @@ public abstract class AbstractRegionNormalizer implements RegionNormalizer { + "number of regions: {}", table, avgRegionSize, table, tableRegions.size()); + // The list of regionInfo from getRegionsOfTable() is ordered by regionName. + // regionName does not necessary guarantee the order by STARTKEY (let's say 'aa1', 'aa1!', + // in order by regionName, it will be 'aa1!' followed by 'aa1'). + // This could result in normalizer merging non-adjacent regions into one and creates overlaps. + // In order to avoid that, sort the list by RegionInfo.COMPARATOR. + tableRegions.sort(RegionInfo.COMPARATOR); final List<NormalizationPlan> plans = new ArrayList<>(); for (int candidateIdx = 0; candidateIdx < tableRegions.size() - 1; candidateIdx++) { final RegionInfo hri = tableRegions.get(candidateIdx); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/normalizer/MergeNormalizationPlan.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/normalizer/MergeNormalizationPlan.java index 7c33661..a2938a9 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/normalizer/MergeNormalizationPlan.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/normalizer/MergeNormalizationPlan.java @@ -69,8 +69,11 @@ public class MergeNormalizationPlan implements NormalizationPlan { public void execute(Admin admin) { LOG.info("Executing merging normalization plan: " + this); try { + // Do not use force=true as corner cases can happen, non adjacent regions, + // merge with a merged child region with no GC done yet, it is going to + // cause all different issues. admin.mergeRegionsAsync(firstRegion.getEncodedNameAsBytes(), - secondRegion.getEncodedNameAsBytes(), true); + secondRegion.getEncodedNameAsBytes(), false); } catch (IOException ex) { LOG.error("Error during region merge: ", ex); } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/normalizer/TestSimpleRegionNormalizerOnCluster.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/normalizer/TestSimpleRegionNormalizerOnCluster.java index 6f7f69e..9199d3f 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/normalizer/TestSimpleRegionNormalizerOnCluster.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/normalizer/TestSimpleRegionNormalizerOnCluster.java @@ -18,6 +18,7 @@ package org.apache.hadoop.hbase.master.normalizer; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import java.io.IOException; import java.util.Collections; @@ -26,7 +27,6 @@ import java.util.List; import org.apache.hadoop.hbase.HBaseClassTestRule; import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HConstants; -import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.MetaTableAccessor; import org.apache.hadoop.hbase.MiniHBaseCluster; import org.apache.hadoop.hbase.NamespaceDescriptor; @@ -35,6 +35,8 @@ import org.apache.hadoop.hbase.client.Admin; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.RegionInfo; import org.apache.hadoop.hbase.client.Table; +import org.apache.hadoop.hbase.client.TableDescriptor; +import org.apache.hadoop.hbase.client.TableDescriptorBuilder; import org.apache.hadoop.hbase.master.HMaster; import org.apache.hadoop.hbase.master.TableNamespaceManager; import org.apache.hadoop.hbase.master.normalizer.NormalizationPlan.PlanType; @@ -93,7 +95,6 @@ public class TestSimpleRegionNormalizerOnCluster { } @Test - @SuppressWarnings("deprecation") public void testRegionNormalizationSplitOnCluster() throws Exception { testRegionNormalizationSplitOnCluster(false); testRegionNormalizationSplitOnCluster(true); @@ -141,9 +142,11 @@ public class TestSimpleRegionNormalizerOnCluster { region.flush(true); } - HTableDescriptor htd = new HTableDescriptor(admin.getTableDescriptor(TABLENAME)); - htd.setNormalizationEnabled(true); - admin.modifyTable(TABLENAME, htd); + final TableDescriptor td = TableDescriptorBuilder.newBuilder(admin.getDescriptor(TABLENAME)) + .setNormalizationEnabled(true) + .build(); + + admin.modifyTable(td); admin.flush(TABLENAME); @@ -179,8 +182,71 @@ public class TestSimpleRegionNormalizerOnCluster { admin.deleteTable(TABLENAME); } + // This test is to make sure that normalizer is only going to merge adjacent regions. + @Test + public void testNormalizerCannotMergeNonAdjacentRegions() throws Exception { + final TableName tableName = TableName.valueOf(name.getMethodName()); + MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); + HMaster m = cluster.getMaster(); + + // create 5 regions with sizes to trigger merge of small regions + final byte[][] keys = { + Bytes.toBytes("aa"), + Bytes.toBytes("aa1"), + Bytes.toBytes("aa1!"), + Bytes.toBytes("aa2") + }; + + try (Table ht = TEST_UTIL.createTable(tableName, FAMILYNAME, keys)) { + // Need to get sorted list of regions here, the order is + // [, "aa"), ["aa", "aa1"), ["aa1", "aa1!"), ["aa1!", "aa2"), ["aa2", ) + List<HRegion> generatedRegions = TEST_UTIL.getHBaseCluster().getRegions(tableName); + generatedRegions.sort(Comparator.comparing(HRegion::getRegionInfo, RegionInfo.COMPARATOR)); + + // Region ["aa", "aa1") and ["aa1!", "aa2") are not adjacent, they are not supposed to + // merged. + HRegion region = generatedRegions.get(0); + generateTestData(region, 3); + region.flush(true); + + region = generatedRegions.get(1); + generateTestData(region, 1); + region.flush(true); + + region = generatedRegions.get(2); + generateTestData(region, 3); + region.flush(true); + + region = generatedRegions.get(3); + generateTestData(region, 1); + region.flush(true); + + region = generatedRegions.get(4); + generateTestData(region, 5); + region.flush(true); + + final TableDescriptor td = TableDescriptorBuilder.newBuilder(admin.getDescriptor(tableName)) + .setNormalizationEnabled(true) + .build(); + admin.modifyTable(td); + admin.flush(tableName); + + assertEquals(5, MetaTableAccessor.getRegionCount(TEST_UTIL.getConnection(), tableName)); + + Thread.sleep(5000); // to let region load to update + + // Compute the plan, no merge plan returned as they are not adjacent. + final List<NormalizationPlan> plans = m.getRegionNormalizer().computePlanForTable(tableName); + assertNull(plans); + } finally { + if (admin.tableExists(tableName)) { + admin.disableTable(tableName); + admin.deleteTable(tableName); + } + } + } + @Test - @SuppressWarnings("deprecation") public void testRegionNormalizationMergeOnCluster() throws Exception { final TableName tableName = TableName.valueOf(name.getMethodName()); MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); @@ -190,7 +256,7 @@ public class TestSimpleRegionNormalizerOnCluster { try (Table ht = TEST_UTIL.createMultiRegionTable(tableName, FAMILYNAME, 5)) { // Need to get sorted list of regions here List<HRegion> generatedRegions = TEST_UTIL.getHBaseCluster().getRegions(tableName); - Collections.sort(generatedRegions, Comparator.comparing(HRegion::getRegionInfo, RegionInfo.COMPARATOR)); + generatedRegions.sort(Comparator.comparing(HRegion::getRegionInfo, RegionInfo.COMPARATOR)); HRegion region = generatedRegions.get(0); generateTestData(region, 1); @@ -213,9 +279,11 @@ public class TestSimpleRegionNormalizerOnCluster { region.flush(true); } - HTableDescriptor htd = new HTableDescriptor(admin.getTableDescriptor(tableName)); - htd.setNormalizationEnabled(true); - admin.modifyTable(tableName, htd); + final TableDescriptor td = TableDescriptorBuilder.newBuilder(admin.getDescriptor(tableName)) + .setNormalizationEnabled(true) + .build(); + + admin.modifyTable(td); admin.flush(tableName);