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.master; 019 020import static org.junit.Assert.assertTrue; 021 022import java.io.IOException; 023import java.util.Collections; 024import java.util.List; 025 026import org.apache.hadoop.hbase.HBaseClassTestRule; 027import org.apache.hadoop.hbase.HBaseTestingUtility; 028import org.apache.hadoop.hbase.HConstants; 029import org.apache.hadoop.hbase.MetaTableAccessor; 030import org.apache.hadoop.hbase.TableName; 031import org.apache.hadoop.hbase.client.RegionInfo; 032import org.apache.hadoop.hbase.client.RegionInfoBuilder; 033import org.apache.hadoop.hbase.testclassification.LargeTests; 034import org.apache.hadoop.hbase.testclassification.MasterTests; 035import org.apache.hadoop.hbase.util.Threads; 036 037import org.junit.AfterClass; 038import org.junit.Assert; 039import org.junit.BeforeClass; 040import org.junit.ClassRule; 041import org.junit.Rule; 042import org.junit.Test; 043import org.junit.experimental.categories.Category; 044import org.junit.rules.TestName; 045 046@Category({MasterTests.class, LargeTests.class}) 047public class TestMetaFixer { 048 @ClassRule 049 public static final HBaseClassTestRule CLASS_RULE = 050 HBaseClassTestRule.forClass(TestMetaFixer.class); 051 @Rule 052 public TestName name = new TestName(); 053 054 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 055 056 @BeforeClass 057 public static void setupBeforeClass() throws Exception { 058 TEST_UTIL.startMiniCluster(); 059 } 060 061 @AfterClass 062 public static void tearDownAfterClass() throws Exception { 063 TEST_UTIL.shutdownMiniCluster(); 064 } 065 066 private void deleteRegion(MasterServices services, RegionInfo ri) throws IOException { 067 MetaTableAccessor.deleteRegionInfo(TEST_UTIL.getConnection(), ri); 068 // Delete it from Master context too else it sticks around. 069 services.getAssignmentManager().getRegionStates().deleteRegion(ri); 070 } 071 072 @Test 073 public void testPlugsHoles() throws IOException { 074 TableName tn = TableName.valueOf(this.name.getMethodName()); 075 TEST_UTIL.createMultiRegionTable(tn, HConstants.CATALOG_FAMILY); 076 List<RegionInfo> ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), tn); 077 MasterServices services = TEST_UTIL.getHBaseCluster().getMaster(); 078 services.getCatalogJanitor().scan(); 079 CatalogJanitor.Report report = services.getCatalogJanitor().getLastReport(); 080 assertTrue(report.isEmpty()); 081 int originalCount = ris.size(); 082 // Remove first, last and middle region. See if hole gets plugged. Table has 26 regions. 083 deleteRegion(services, ris.get(ris.size() -1)); 084 deleteRegion(services, ris.get(3)); 085 deleteRegion(services, ris.get(0)); 086 services.getCatalogJanitor().scan(); 087 report = services.getCatalogJanitor().getLastReport(); 088 Assert.assertEquals(report.toString(), 3, report.getHoles().size()); 089 MetaFixer fixer = new MetaFixer(services); 090 fixer.fixHoles(report); 091 services.getCatalogJanitor().scan(); 092 report = services.getCatalogJanitor().getLastReport(); 093 assertTrue(report.toString(), report.isEmpty()); 094 // Disable and reenable so the added regions get reassigned. 095 TEST_UTIL.getAdmin().disableTable(tn); 096 TEST_UTIL.getAdmin().enableTable(tn); 097 ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), tn); 098 Assert.assertEquals(originalCount, ris.size()); 099 } 100 101 /** 102 * Just make sure running fixMeta does right thing for the case 103 * of a single-region Table where the region gets dropped. 104 * There is nothing much we can do. We can't restore what 105 * we don't know about (at least from a read of hbase:meta). 106 */ 107 @Test 108 public void testOneRegionTable() throws IOException { 109 TableName tn = TableName.valueOf(this.name.getMethodName()); 110 TEST_UTIL.createTable(tn, HConstants.CATALOG_FAMILY); 111 List<RegionInfo> ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), tn); 112 MasterServices services = TEST_UTIL.getHBaseCluster().getMaster(); 113 services.getCatalogJanitor().scan(); 114 deleteRegion(services, ris.get(0)); 115 services.getCatalogJanitor().scan(); 116 CatalogJanitor.Report report = services.getCatalogJanitor().getLastReport(); 117 ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), tn); 118 assertTrue(ris.isEmpty()); 119 MetaFixer fixer = new MetaFixer(services); 120 fixer.fixHoles(report); 121 report = services.getCatalogJanitor().getLastReport(); 122 assertTrue(report.isEmpty()); 123 ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), tn); 124 Assert.assertEquals(0, ris.size()); 125 } 126 127 private static void makeOverlap(MasterServices services, RegionInfo a, RegionInfo b) 128 throws IOException { 129 RegionInfo overlapRegion = RegionInfoBuilder.newBuilder(a.getTable()). 130 setStartKey(a.getStartKey()). 131 setEndKey(b.getEndKey()). 132 build(); 133 MetaTableAccessor.putsToMetaTable(services.getConnection(), 134 Collections.singletonList(MetaTableAccessor.makePutFromRegionInfo(overlapRegion, 135 System.currentTimeMillis()))); 136 // TODO: Add checks at assign time to PREVENT being able to assign over existing assign. 137 services.getAssignmentManager().assign(overlapRegion); 138 } 139 140 @Test 141 public void testOverlap() throws IOException { 142 TableName tn = TableName.valueOf(this.name.getMethodName()); 143 TEST_UTIL.createMultiRegionTable(tn, HConstants.CATALOG_FAMILY); 144 List<RegionInfo> ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), tn); 145 assertTrue(ris.size() > 5); 146 MasterServices services = TEST_UTIL.getHBaseCluster().getMaster(); 147 services.getCatalogJanitor().scan(); 148 CatalogJanitor.Report report = services.getCatalogJanitor().getLastReport(); 149 assertTrue(report.isEmpty()); 150 // Make a simple overlap spanning second and third region. 151 makeOverlap(services, ris.get(1), ris.get(3)); 152 makeOverlap(services, ris.get(2), ris.get(3)); 153 makeOverlap(services, ris.get(2), ris.get(4)); 154 Threads.sleep(10000); 155 services.getCatalogJanitor().scan(); 156 report = services.getCatalogJanitor().getLastReport(); 157 Assert.assertEquals(6, report.getOverlaps().size()); 158 Assert.assertEquals(1, MetaFixer.calculateMerges(10, report.getOverlaps()).size()); 159 MetaFixer fixer = new MetaFixer(services); 160 fixer.fixOverlaps(report); 161 while (true) { 162 services.getCatalogJanitor().scan(); 163 report = services.getCatalogJanitor().getLastReport(); 164 if (report.isEmpty()) { 165 break; 166 } 167 Threads.sleep(10); 168 } 169 assertTrue(report.toString(), report.isEmpty()); 170 } 171}