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 155 @Test 156 public void testForDisabledTable() throws Exception { 157 TableName tableName = TableName.valueOf("testForDisabledTable"); 158 RegionInfo hri = createRegionInfo(tableName, 1); 159 String regionName = hri.getRegionNameAsString(); 160 rsDispatcher.setMockRsExecutor(new GoodRsExecutor()); 161 Future<byte[]> future = submitProcedure(createAssignProcedure(hri)); 162 waitOnFuture(future); 163 164 List<ServerName> serverNames = master.getServerManager().getOnlineServersList(); 165 assertEquals(NSERVERS, serverNames.size()); 166 167 hbckChore.choreForTesting(); 168 Map<String, Pair<ServerName, List<ServerName>>> inconsistentRegions = 169 hbckChore.getLastReport().getInconsistentRegions(); 170 assertTrue(inconsistentRegions.containsKey(regionName)); 171 Pair<ServerName, List<ServerName>> pair = inconsistentRegions.get(regionName); 172 ServerName locationInMeta = pair.getFirst(); 173 List<ServerName> reportedRegionServers = pair.getSecond(); 174 assertTrue(serverNames.contains(locationInMeta)); 175 assertEquals(0, reportedRegionServers.size()); 176 177 // Set table state to disabled, then not in inconsistent regions. 178 TableStateManager tableStateManager = master.getTableStateManager(); 179 Mockito.when(tableStateManager.isTableState(tableName, TableState.State.DISABLED)) 180 .thenReturn(true); 181 hbckChore.choreForTesting(); 182 inconsistentRegions = hbckChore.getLastReport().getInconsistentRegions(); 183 assertFalse(inconsistentRegions.containsKey(regionName)); 184 } 185 186 @Test 187 public void testForSplitParent() throws Exception { 188 TableName tableName = TableName.valueOf("testForSplitParent"); 189 RegionInfo hri = RegionInfoBuilder.newBuilder(tableName).setStartKey(Bytes.toBytes(0)) 190 .setEndKey(Bytes.toBytes(1)).setSplit(true).setOffline(true).setRegionId(0).build(); 191 String regionName = hri.getEncodedName(); 192 rsDispatcher.setMockRsExecutor(new GoodRsExecutor()); 193 Future<byte[]> future = submitProcedure(createAssignProcedure(hri)); 194 waitOnFuture(future); 195 196 List<ServerName> serverNames = master.getServerManager().getOnlineServersList(); 197 assertEquals(NSERVERS, serverNames.size()); 198 199 hbckChore.choreForTesting(); 200 Map<String, Pair<ServerName, List<ServerName>>> inconsistentRegions = 201 hbckChore.getLastReport().getInconsistentRegions(); 202 assertFalse(inconsistentRegions.containsKey(regionName)); 203 } 204 205 @Test 206 public void testOrphanRegionsOnFS() throws Exception { 207 TableName tableName = TableName.valueOf("testOrphanRegionsOnFS"); 208 RegionInfo regionInfo = RegionInfoBuilder.newBuilder(tableName).build(); 209 Configuration conf = util.getConfiguration(); 210 211 hbckChore.choreForTesting(); 212 assertEquals(0, hbckChore.getLastReport().getOrphanRegionsOnFS().size()); 213 214 HRegion.createRegionDir(conf, regionInfo, CommonFSUtils.getRootDir(conf)); 215 hbckChore.choreForTesting(); 216 assertEquals(1, hbckChore.getLastReport().getOrphanRegionsOnFS().size()); 217 assertTrue( 218 hbckChore.getLastReport().getOrphanRegionsOnFS().containsKey(regionInfo.getEncodedName())); 219 220 FSUtils.deleteRegionDir(conf, regionInfo); 221 hbckChore.choreForTesting(); 222 assertEquals(0, hbckChore.getLastReport().getOrphanRegionsOnFS().size()); 223 } 224 225 @Test 226 public void testChoreDisable() { 227 // The way to disable to chore is to set hbase.master.hbck.chore.interval <= 0 228 // When the interval is > 0, the chore should run. 229 Instant lastRunTime = Optional.ofNullable(hbckChore.getLastReport()) 230 .map(HbckReport::getCheckingEndTimestamp).orElse(null); 231 hbckChore.choreForTesting(); 232 Instant thisRunTime = Optional.ofNullable(hbckChore.getLastReport()) 233 .map(HbckReport::getCheckingEndTimestamp).orElse(null); 234 assertNotNull(thisRunTime); 235 assertNotEquals(lastRunTime, thisRunTime); 236 237 // When the interval <= 0, the chore shouldn't run 238 master.getConfiguration().setInt("hbase.master.hbck.chore.interval", 0); 239 HbckChore hbckChoreWithChangedConf = new HbckChore(master); 240 hbckChoreWithChangedConf.choreForTesting(); 241 assertNull(hbckChoreWithChangedConf.getLastReport()); 242 } 243}