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.assignment; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertNotEquals; 023import static org.junit.Assert.assertNotNull; 024import static org.junit.Assert.assertNull; 025import static org.junit.Assert.assertTrue; 026 027import java.time.Instant; 028import java.util.Collections; 029import java.util.List; 030import java.util.Map; 031import java.util.Optional; 032import java.util.concurrent.Future; 033import org.apache.hadoop.conf.Configuration; 034import org.apache.hadoop.hbase.HBaseClassTestRule; 035import org.apache.hadoop.hbase.ServerName; 036import org.apache.hadoop.hbase.TableName; 037import org.apache.hadoop.hbase.client.RegionInfo; 038import org.apache.hadoop.hbase.client.RegionInfoBuilder; 039import org.apache.hadoop.hbase.client.TableState; 040import org.apache.hadoop.hbase.master.TableStateManager; 041import org.apache.hadoop.hbase.master.hbck.HbckChore; 042import org.apache.hadoop.hbase.master.hbck.HbckReport; 043import org.apache.hadoop.hbase.regionserver.HRegion; 044import org.apache.hadoop.hbase.testclassification.MasterTests; 045import org.apache.hadoop.hbase.testclassification.MediumTests; 046import org.apache.hadoop.hbase.util.Bytes; 047import org.apache.hadoop.hbase.util.CommonFSUtils; 048import org.apache.hadoop.hbase.util.FSUtils; 049import org.apache.hadoop.hbase.util.Pair; 050import org.junit.Before; 051import org.junit.ClassRule; 052import org.junit.Test; 053import org.junit.experimental.categories.Category; 054import org.mockito.Mockito; 055 056@Category({ MasterTests.class, MediumTests.class }) 057public class TestHbckChore extends TestAssignmentManagerBase { 058 059 @ClassRule 060 public static final HBaseClassTestRule CLASS_RULE = 061 HBaseClassTestRule.forClass(TestHbckChore.class); 062 063 private HbckChore hbckChore; 064 065 @Before 066 public void setUp() throws Exception { 067 super.setUp(); 068 hbckChore = new HbckChore(master); 069 } 070 071 @Test 072 public void testForMeta() { 073 byte[] metaRegionNameAsBytes = RegionInfoBuilder.FIRST_META_REGIONINFO.getRegionName(); 074 String metaRegionName = RegionInfoBuilder.FIRST_META_REGIONINFO.getRegionNameAsString(); 075 List<ServerName> serverNames = master.getServerManager().getOnlineServersList(); 076 assertEquals(NSERVERS, serverNames.size()); 077 078 hbckChore.choreForTesting(); 079 Map<String, Pair<ServerName, List<ServerName>>> inconsistentRegions = 080 hbckChore.getLastReport().getInconsistentRegions(); 081 082 // Test for case1: Master thought this region opened, but no regionserver reported it. 083 assertTrue(inconsistentRegions.containsKey(metaRegionName)); 084 Pair<ServerName, List<ServerName>> pair = inconsistentRegions.get(metaRegionName); 085 ServerName locationInMeta = pair.getFirst(); 086 List<ServerName> reportedRegionServers = pair.getSecond(); 087 assertTrue(serverNames.contains(locationInMeta)); 088 assertEquals(0, reportedRegionServers.size()); 089 090 // Reported right region location. Then not in problematic regions. 091 am.reportOnlineRegions(locationInMeta, Collections.singleton(metaRegionNameAsBytes)); 092 hbckChore.choreForTesting(); 093 inconsistentRegions = hbckChore.getLastReport().getInconsistentRegions(); 094 assertFalse(inconsistentRegions.containsKey(metaRegionName)); 095 } 096 097 @Test 098 public void testForUserTable() throws Exception { 099 TableName tableName = TableName.valueOf("testForUserTable"); 100 RegionInfo hri = createRegionInfo(tableName, 1); 101 String regionName = hri.getRegionNameAsString(); 102 rsDispatcher.setMockRsExecutor(new GoodRsExecutor()); 103 Future<byte[]> future = submitProcedure(createAssignProcedure(hri)); 104 waitOnFuture(future); 105 106 List<ServerName> serverNames = master.getServerManager().getOnlineServersList(); 107 assertEquals(NSERVERS, serverNames.size()); 108 109 // Test for case1: Master thought this region opened, but no regionserver reported it. 110 hbckChore.choreForTesting(); 111 Map<String, Pair<ServerName, List<ServerName>>> inconsistentRegions = 112 hbckChore.getLastReport().getInconsistentRegions(); 113 assertTrue(inconsistentRegions.containsKey(regionName)); 114 Pair<ServerName, List<ServerName>> pair = inconsistentRegions.get(regionName); 115 ServerName locationInMeta = pair.getFirst(); 116 List<ServerName> reportedRegionServers = pair.getSecond(); 117 assertTrue(serverNames.contains(locationInMeta)); 118 assertEquals(0, reportedRegionServers.size()); 119 120 // Test for case2: Master thought this region opened on Server1, but regionserver reported 121 // Server2 122 final ServerName tempLocationInMeta = locationInMeta; 123 final ServerName anotherServer = 124 serverNames.stream().filter(s -> !s.equals(tempLocationInMeta)).findFirst().get(); 125 am.reportOnlineRegions(anotherServer, Collections.singleton(hri.getRegionName())); 126 hbckChore.choreForTesting(); 127 inconsistentRegions = hbckChore.getLastReport().getInconsistentRegions(); 128 assertTrue(inconsistentRegions.containsKey(regionName)); 129 pair = inconsistentRegions.get(regionName); 130 locationInMeta = pair.getFirst(); 131 reportedRegionServers = pair.getSecond(); 132 assertEquals(1, reportedRegionServers.size()); 133 assertFalse(reportedRegionServers.contains(locationInMeta)); 134 assertTrue(reportedRegionServers.contains(anotherServer)); 135 136 // Test for case3: More than one regionservers reported opened this region. 137 am.reportOnlineRegions(locationInMeta, Collections.singleton(hri.getRegionName())); 138 hbckChore.choreForTesting(); 139 inconsistentRegions = hbckChore.getLastReport().getInconsistentRegions(); 140 assertTrue(inconsistentRegions.containsKey(regionName)); 141 pair = inconsistentRegions.get(regionName); 142 locationInMeta = pair.getFirst(); 143 reportedRegionServers = pair.getSecond(); 144 assertEquals(2, reportedRegionServers.size()); 145 assertTrue(reportedRegionServers.contains(locationInMeta)); 146 assertTrue(reportedRegionServers.contains(anotherServer)); 147 148 // Reported right region location, then not in inconsistent regions. 149 am.reportOnlineRegions(anotherServer, Collections.emptySet()); 150 hbckChore.choreForTesting(); 151 inconsistentRegions = hbckChore.getLastReport().getInconsistentRegions(); 152 assertFalse(inconsistentRegions.containsKey(regionName)); 153 154 // Test for case4: No region location for a previously reported region. Probably due to 155 // TRSP bug or bypass. 156 am.offlineRegion(hri); 157 hbckChore.choreForTesting(); 158 inconsistentRegions = hbckChore.getLastReport().getInconsistentRegions(); 159 assertTrue(inconsistentRegions.containsKey(regionName)); 160 } 161 162 @Test 163 public void testForDisabledTable() throws Exception { 164 TableName tableName = TableName.valueOf("testForDisabledTable"); 165 RegionInfo hri = createRegionInfo(tableName, 1); 166 String regionName = hri.getRegionNameAsString(); 167 rsDispatcher.setMockRsExecutor(new GoodRsExecutor()); 168 Future<byte[]> future = submitProcedure(createAssignProcedure(hri)); 169 waitOnFuture(future); 170 171 List<ServerName> serverNames = master.getServerManager().getOnlineServersList(); 172 assertEquals(NSERVERS, serverNames.size()); 173 174 hbckChore.choreForTesting(); 175 Map<String, Pair<ServerName, List<ServerName>>> inconsistentRegions = 176 hbckChore.getLastReport().getInconsistentRegions(); 177 assertTrue(inconsistentRegions.containsKey(regionName)); 178 Pair<ServerName, List<ServerName>> pair = inconsistentRegions.get(regionName); 179 ServerName locationInMeta = pair.getFirst(); 180 List<ServerName> reportedRegionServers = pair.getSecond(); 181 assertTrue(serverNames.contains(locationInMeta)); 182 assertEquals(0, reportedRegionServers.size()); 183 184 // Set table state to disabled, then not in inconsistent regions. 185 TableStateManager tableStateManager = master.getTableStateManager(); 186 Mockito.when(tableStateManager.isTableState(tableName, TableState.State.DISABLED)) 187 .thenReturn(true); 188 hbckChore.choreForTesting(); 189 inconsistentRegions = hbckChore.getLastReport().getInconsistentRegions(); 190 assertFalse(inconsistentRegions.containsKey(regionName)); 191 } 192 193 @Test 194 public void testForSplitParent() throws Exception { 195 TableName tableName = TableName.valueOf("testForSplitParent"); 196 RegionInfo hri = RegionInfoBuilder.newBuilder(tableName).setStartKey(Bytes.toBytes(0)) 197 .setEndKey(Bytes.toBytes(1)).setSplit(true).setOffline(true).setRegionId(0).build(); 198 String regionName = hri.getEncodedName(); 199 rsDispatcher.setMockRsExecutor(new GoodRsExecutor()); 200 Future<byte[]> future = submitProcedure(createAssignProcedure(hri)); 201 waitOnFuture(future); 202 203 List<ServerName> serverNames = master.getServerManager().getOnlineServersList(); 204 assertEquals(NSERVERS, serverNames.size()); 205 206 hbckChore.choreForTesting(); 207 Map<String, Pair<ServerName, List<ServerName>>> inconsistentRegions = 208 hbckChore.getLastReport().getInconsistentRegions(); 209 assertFalse(inconsistentRegions.containsKey(regionName)); 210 } 211 212 @Test 213 public void testOrphanRegionsOnFS() throws Exception { 214 TableName tableName = TableName.valueOf("testOrphanRegionsOnFS"); 215 RegionInfo regionInfo = RegionInfoBuilder.newBuilder(tableName).build(); 216 Configuration conf = util.getConfiguration(); 217 218 hbckChore.choreForTesting(); 219 assertEquals(0, hbckChore.getLastReport().getOrphanRegionsOnFS().size()); 220 221 HRegion.createRegionDir(conf, regionInfo, CommonFSUtils.getRootDir(conf)); 222 hbckChore.choreForTesting(); 223 assertEquals(1, hbckChore.getLastReport().getOrphanRegionsOnFS().size()); 224 assertTrue( 225 hbckChore.getLastReport().getOrphanRegionsOnFS().containsKey(regionInfo.getEncodedName())); 226 227 FSUtils.deleteRegionDir(conf, regionInfo); 228 hbckChore.choreForTesting(); 229 assertEquals(0, hbckChore.getLastReport().getOrphanRegionsOnFS().size()); 230 } 231 232 @Test 233 public void testChoreDisable() { 234 // The way to disable to chore is to set hbase.master.hbck.chore.interval <= 0 235 // When the interval is > 0, the chore should run. 236 Instant lastRunTime = Optional.ofNullable(hbckChore.getLastReport()) 237 .map(HbckReport::getCheckingEndTimestamp).orElse(null); 238 hbckChore.choreForTesting(); 239 Instant thisRunTime = Optional.ofNullable(hbckChore.getLastReport()) 240 .map(HbckReport::getCheckingEndTimestamp).orElse(null); 241 assertNotNull(thisRunTime); 242 assertNotEquals(lastRunTime, thisRunTime); 243 244 // When the interval <= 0, the chore shouldn't run 245 master.getConfiguration().setInt("hbase.master.hbck.chore.interval", 0); 246 HbckChore hbckChoreWithChangedConf = new HbckChore(master); 247 hbckChoreWithChangedConf.choreForTesting(); 248 assertNull(hbckChoreWithChangedConf.getLastReport()); 249 } 250}