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.assertEquals; 021import static org.junit.Assert.assertTrue; 022 023import java.io.IOException; 024import java.util.Collections; 025import java.util.List; 026 027import org.apache.hadoop.hbase.HBaseClassTestRule; 028import org.apache.hadoop.hbase.HBaseTestingUtility; 029import org.apache.hadoop.hbase.HConstants; 030import org.apache.hadoop.hbase.MetaTableAccessor; 031import org.apache.hadoop.hbase.TableName; 032import org.apache.hadoop.hbase.client.RegionInfo; 033import org.apache.hadoop.hbase.client.RegionInfoBuilder; 034import org.apache.hadoop.hbase.testclassification.LargeTests; 035import org.apache.hadoop.hbase.testclassification.MasterTests; 036import org.apache.hadoop.hbase.util.Threads; 037 038import org.junit.AfterClass; 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 int initialSize = services.getAssignmentManager().getRegionStates().getRegionStates().size(); 079 services.getCatalogJanitor().scan(); 080 CatalogJanitor.Report report = services.getCatalogJanitor().getLastReport(); 081 assertTrue(report.isEmpty()); 082 int originalCount = ris.size(); 083 // Remove first, last and middle region. See if hole gets plugged. Table has 26 regions. 084 deleteRegion(services, ris.get(ris.size() -1)); 085 deleteRegion(services, ris.get(3)); 086 deleteRegion(services, ris.get(0)); 087 assertEquals(initialSize - 3, 088 services.getAssignmentManager().getRegionStates().getRegionStates().size()); 089 services.getCatalogJanitor().scan(); 090 report = services.getCatalogJanitor().getLastReport(); 091 assertEquals(report.toString(), 3, report.getHoles().size()); 092 MetaFixer fixer = new MetaFixer(services); 093 fixer.fixHoles(report); 094 services.getCatalogJanitor().scan(); 095 report = services.getCatalogJanitor().getLastReport(); 096 assertTrue(report.toString(), report.isEmpty()); 097 assertEquals(initialSize, 098 services.getAssignmentManager().getRegionStates().getRegionStates().size()); 099 // Disable and reenable so the added regions get reassigned. 100 TEST_UTIL.getAdmin().disableTable(tn); 101 TEST_UTIL.getAdmin().enableTable(tn); 102 ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), tn); 103 assertEquals(originalCount, ris.size()); 104 } 105 106 /** 107 * Just make sure running fixMeta does right thing for the case 108 * of a single-region Table where the region gets dropped. 109 * There is nothing much we can do. We can't restore what 110 * we don't know about (at least from a read of hbase:meta). 111 */ 112 @Test 113 public void testOneRegionTable() throws IOException { 114 TableName tn = TableName.valueOf(this.name.getMethodName()); 115 TEST_UTIL.createTable(tn, HConstants.CATALOG_FAMILY); 116 List<RegionInfo> ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), tn); 117 MasterServices services = TEST_UTIL.getHBaseCluster().getMaster(); 118 services.getCatalogJanitor().scan(); 119 deleteRegion(services, ris.get(0)); 120 services.getCatalogJanitor().scan(); 121 CatalogJanitor.Report report = services.getCatalogJanitor().getLastReport(); 122 ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), tn); 123 assertTrue(ris.isEmpty()); 124 MetaFixer fixer = new MetaFixer(services); 125 fixer.fixHoles(report); 126 report = services.getCatalogJanitor().getLastReport(); 127 assertTrue(report.isEmpty()); 128 ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), tn); 129 assertEquals(0, ris.size()); 130 } 131 132 private static void makeOverlap(MasterServices services, RegionInfo a, RegionInfo b) 133 throws IOException { 134 RegionInfo overlapRegion = RegionInfoBuilder.newBuilder(a.getTable()). 135 setStartKey(a.getStartKey()). 136 setEndKey(b.getEndKey()). 137 build(); 138 MetaTableAccessor.putsToMetaTable(services.getConnection(), 139 Collections.singletonList(MetaTableAccessor.makePutFromRegionInfo(overlapRegion, 140 System.currentTimeMillis()))); 141 // TODO: Add checks at assign time to PREVENT being able to assign over existing assign. 142 services.getAssignmentManager().assign(overlapRegion); 143 } 144 145 @Test 146 public void testOverlap() throws IOException { 147 TableName tn = TableName.valueOf(this.name.getMethodName()); 148 TEST_UTIL.createMultiRegionTable(tn, HConstants.CATALOG_FAMILY); 149 List<RegionInfo> ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), tn); 150 assertTrue(ris.size() > 5); 151 MasterServices services = TEST_UTIL.getHBaseCluster().getMaster(); 152 services.getCatalogJanitor().scan(); 153 CatalogJanitor.Report report = services.getCatalogJanitor().getLastReport(); 154 assertTrue(report.isEmpty()); 155 // Make a simple overlap spanning second and third region. 156 makeOverlap(services, ris.get(1), ris.get(3)); 157 makeOverlap(services, ris.get(2), ris.get(3)); 158 makeOverlap(services, ris.get(2), ris.get(4)); 159 Threads.sleep(10000); 160 services.getCatalogJanitor().scan(); 161 report = services.getCatalogJanitor().getLastReport(); 162 assertEquals(6, report.getOverlaps().size()); 163 assertEquals(1, MetaFixer.calculateMerges(10, report.getOverlaps()).size()); 164 MetaFixer fixer = new MetaFixer(services); 165 fixer.fixOverlaps(report); 166 while (true) { 167 services.getCatalogJanitor().scan(); 168 report = services.getCatalogJanitor().getLastReport(); 169 if (report.isEmpty()) { 170 break; 171 } 172 Threads.sleep(10); 173 } 174 assertTrue(report.toString(), report.isEmpty()); 175 } 176}