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