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.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertFalse; 022import static org.junit.jupiter.api.Assertions.assertNotNull; 023import static org.junit.jupiter.api.Assertions.assertNull; 024import static org.junit.jupiter.api.Assertions.assertTrue; 025 026import java.util.List; 027import org.apache.hadoop.hbase.HBaseTestingUtil; 028import org.apache.hadoop.hbase.TableName; 029import org.apache.hadoop.hbase.client.RegionInfo; 030import org.apache.hadoop.hbase.client.Table; 031import org.apache.hadoop.hbase.master.assignment.AssignmentManager; 032import org.apache.hadoop.hbase.master.assignment.RegionStateNode; 033import org.apache.hadoop.hbase.testclassification.MasterTests; 034import org.apache.hadoop.hbase.testclassification.MediumTests; 035import org.apache.hadoop.hbase.util.Bytes; 036import org.junit.jupiter.api.AfterAll; 037import org.junit.jupiter.api.BeforeAll; 038import org.junit.jupiter.api.Tag; 039import org.junit.jupiter.api.Test; 040 041import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos; 042 043/** 044 * Test for HBASE-30142. Verifies that {@code scheduleSCPsForUnknownServers} (i.e. the 045 * recoverUnknown HBCK command) does not throw a NullPointerException when a region's 046 * {@code regionLocation} is null while its state is something other than OFFLINE. 047 * <p/> 048 * Before HBASE-30142, {@link ServerManager#isServerUnknown(org.apache.hadoop.hbase.ServerName)} 049 * returned {@code true} for a {@code null} server name, so a region whose location had been 050 * temporarily nulled (e.g. between region transitions or while marking it FAILED_OPEN / 051 * ABNORMALLY_CLOSED) was treated as living on an "unknown" server. The downstream call to 052 * {@code shouldSubmitSCP(null)} then dereferenced the null and crashed. 053 */ 054@Tag(MasterTests.TAG) 055@Tag(MediumTests.TAG) 056public class TestRecoverUnknownWithNullRegionLocation { 057 058 private static HBaseTestingUtil UTIL; 059 private static final TableName TABLE_NAME = 060 TableName.valueOf("TestRecoverUnknownWithNullRegionLocation"); 061 private static final byte[] FAMILY = Bytes.toBytes("cf"); 062 063 @BeforeAll 064 public static void setUpBeforeClass() throws Exception { 065 UTIL = new HBaseTestingUtil(); 066 UTIL.startMiniCluster(2); 067 } 068 069 @AfterAll 070 public static void tearDownAfterClass() throws Exception { 071 if (UTIL != null) { 072 UTIL.shutdownMiniCluster(); 073 } 074 } 075 076 /** 077 * Drive the region into ABNORMALLY_CLOSED through the 078 * {@link AssignmentManager#regionClosedAbnormally(RegionStateNode)} path (the same path SCP 079 * uses). That method also nulls {@code regionLocation} while leaving state non-OFFLINE. Then call 080 * {@code scheduleSCPsForUnknownServers} and assert no NPE. 081 */ 082 @Test 083 public void testRecoverUnknownWithAbnormallyClosedRegion() throws Exception { 084 try (Table ignored = UTIL.createTable(TABLE_NAME, FAMILY)) { 085 UTIL.waitTableAvailable(TABLE_NAME); 086 087 HMaster master = UTIL.getMiniHBaseCluster().getMaster(); 088 AssignmentManager am = master.getAssignmentManager(); 089 List<RegionInfo> regions = am.getTableRegions(TABLE_NAME, true); 090 assertFalse(regions.isEmpty(), "expected at least one region"); 091 RegionStateNode node = am.getRegionStates().getRegionStateNode(regions.get(0)); 092 assertNotNull(node, "expected a region state node for the test region"); 093 094 node.lock(); 095 try { 096 am.regionClosedAbnormally(node).get(); 097 assertTrue(node.isInState(RegionState.State.ABNORMALLY_CLOSED), 098 "regionClosedAbnormally must move state to ABNORMALLY_CLOSED"); 099 assertNull(node.getRegionLocation(), "regionClosedAbnormally must null out the location"); 100 MasterProtos.ScheduleSCPsForUnknownServersResponse response = 101 master.getMasterRpcServices().scheduleSCPsForUnknownServers(null, 102 MasterProtos.ScheduleSCPsForUnknownServersRequest.newBuilder().build()); 103 assertEquals(0, response.getPidCount(), 104 "no SCPs should be scheduled for an ABNORMALLY_CLOSED region with null location"); 105 } finally { 106 node.unlock(); 107 } 108 } 109 } 110}