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