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.regionserver; 019 020import static org.junit.Assert.assertTrue; 021 022import java.io.IOException; 023import org.apache.hadoop.hbase.HBaseClassTestRule; 024import org.apache.hadoop.hbase.HBaseTestingUtil; 025import org.apache.hadoop.hbase.HConstants; 026import org.apache.hadoop.hbase.NotServingRegionException; 027import org.apache.hadoop.hbase.ServerName; 028import org.apache.hadoop.hbase.TableName; 029import org.apache.hadoop.hbase.client.Put; 030import org.apache.hadoop.hbase.client.RegionInfo; 031import org.apache.hadoop.hbase.client.RegionLocator; 032import org.apache.hadoop.hbase.client.Table; 033import org.apache.hadoop.hbase.client.TableDescriptor; 034import org.apache.hadoop.hbase.master.HMaster; 035import org.apache.hadoop.hbase.regionserver.handler.OpenRegionHandler; 036import org.apache.hadoop.hbase.testclassification.MediumTests; 037import org.apache.hadoop.hbase.testclassification.RegionServerTests; 038import org.apache.hadoop.hbase.util.Bytes; 039import org.apache.hadoop.hbase.util.JVMClusterUtil; 040import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread; 041import org.apache.hadoop.hbase.util.Threads; 042import org.junit.AfterClass; 043import org.junit.Assert; 044import org.junit.BeforeClass; 045import org.junit.ClassRule; 046import org.junit.Test; 047import org.junit.experimental.categories.Category; 048import org.slf4j.Logger; 049import org.slf4j.LoggerFactory; 050 051import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 052import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter; 053import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos; 054import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CloseRegionRequest; 055 056/** 057 * Tests on the region server, without the master. 058 */ 059@Category({ RegionServerTests.class, MediumTests.class }) 060public class TestRegionServerNoMaster { 061 062 @ClassRule 063 public static final HBaseClassTestRule CLASS_RULE = 064 HBaseClassTestRule.forClass(TestRegionServerNoMaster.class); 065 066 private static final Logger LOG = LoggerFactory.getLogger(TestRegionServerNoMaster.class); 067 private static final int NB_SERVERS = 1; 068 private static Table table; 069 private static final byte[] row = Bytes.toBytes("ee"); 070 071 private static RegionInfo hri; 072 073 private static byte[] regionName; 074 private static final HBaseTestingUtil HTU = new HBaseTestingUtil(); 075 076 @BeforeClass 077 public static void before() throws Exception { 078 HTU.startMiniCluster(NB_SERVERS); 079 final TableName tableName = TableName.valueOf(TestRegionServerNoMaster.class.getSimpleName()); 080 081 // Create table then get the single region for our new table. 082 table = HTU.createTable(tableName, HConstants.CATALOG_FAMILY); 083 Put p = new Put(row); 084 p.addColumn(HConstants.CATALOG_FAMILY, row, row); 085 table.put(p); 086 087 try (RegionLocator locator = HTU.getConnection().getRegionLocator(tableName)) { 088 hri = locator.getRegionLocation(row, false).getRegion(); 089 } 090 regionName = hri.getRegionName(); 091 092 stopMasterAndCacheMetaLocation(HTU); 093 } 094 095 public static void stopMasterAndCacheMetaLocation(HBaseTestingUtil HTU) 096 throws IOException, InterruptedException { 097 // cache meta location, so we will not go to master to lookup meta region location 098 for (JVMClusterUtil.RegionServerThread t : HTU.getMiniHBaseCluster().getRegionServerThreads()) { 099 try (RegionLocator locator = 100 t.getRegionServer().getConnection().getRegionLocator(TableName.META_TABLE_NAME)) { 101 locator.getAllRegionLocations(); 102 } 103 } 104 try (RegionLocator locator = HTU.getConnection().getRegionLocator(TableName.META_TABLE_NAME)) { 105 locator.getAllRegionLocations(); 106 } 107 // Stop master 108 HMaster master = HTU.getHBaseCluster().getMaster(); 109 Thread masterThread = HTU.getHBaseCluster().getMasterThread(); 110 master.stopMaster(); 111 112 LOG.info("Waiting until master thread exits"); 113 while (masterThread != null && masterThread.isAlive()) { 114 Threads.sleep(100); 115 } 116 117 HRegionServer.TEST_SKIP_REPORTING_TRANSITION = true; 118 } 119 120 /** 121 * Flush the given region in the mini cluster. Since no master, we cannot use HBaseAdmin.flush() 122 */ 123 public static void flushRegion(HBaseTestingUtil HTU, RegionInfo regionInfo) throws IOException { 124 for (RegionServerThread rst : HTU.getMiniHBaseCluster().getRegionServerThreads()) { 125 HRegion region = rst.getRegionServer().getRegionByEncodedName(regionInfo.getEncodedName()); 126 if (region != null) { 127 region.flush(true); 128 return; 129 } 130 } 131 throw new IOException("Region to flush cannot be found"); 132 } 133 134 @AfterClass 135 public static void afterClass() throws Exception { 136 HRegionServer.TEST_SKIP_REPORTING_TRANSITION = false; 137 if (table != null) { 138 table.close(); 139 } 140 HTU.shutdownMiniCluster(); 141 } 142 143 private static HRegionServer getRS() { 144 return HTU.getHBaseCluster().getLiveRegionServerThreads().get(0).getRegionServer(); 145 } 146 147 public static void openRegion(HBaseTestingUtil HTU, HRegionServer rs, RegionInfo hri) 148 throws Exception { 149 AdminProtos.OpenRegionRequest orr = 150 RequestConverter.buildOpenRegionRequest(rs.getServerName(), hri, null); 151 AdminProtos.OpenRegionResponse responseOpen = rs.getRpcServices().openRegion(null, orr); 152 153 Assert.assertTrue(responseOpen.getOpeningStateCount() == 1); 154 Assert.assertTrue(responseOpen.getOpeningState(0) 155 .equals(AdminProtos.OpenRegionResponse.RegionOpeningState.OPENED)); 156 157 checkRegionIsOpened(HTU, rs, hri); 158 } 159 160 public static void checkRegionIsOpened(HBaseTestingUtil HTU, HRegionServer rs, RegionInfo hri) 161 throws Exception { 162 while (!rs.getRegionsInTransitionInRS().isEmpty()) { 163 Thread.sleep(1); 164 } 165 166 Assert.assertTrue(rs.getRegion(hri.getRegionName()).isAvailable()); 167 } 168 169 public static void closeRegion(HBaseTestingUtil HTU, HRegionServer rs, RegionInfo hri) 170 throws Exception { 171 AdminProtos.CloseRegionRequest crr = 172 ProtobufUtil.buildCloseRegionRequest(rs.getServerName(), hri.getRegionName()); 173 AdminProtos.CloseRegionResponse responseClose = rs.getRpcServices().closeRegion(null, crr); 174 Assert.assertTrue(responseClose.getClosed()); 175 checkRegionIsClosed(HTU, rs, hri); 176 } 177 178 public static void checkRegionIsClosed(HBaseTestingUtil HTU, HRegionServer rs, RegionInfo hri) 179 throws Exception { 180 while (!rs.getRegionsInTransitionInRS().isEmpty()) { 181 Thread.sleep(1); 182 } 183 184 try { 185 Assert.assertFalse(rs.getRegion(hri.getRegionName()).isAvailable()); 186 } catch (NotServingRegionException expected) { 187 // That's how it work: if the region is closed we have an exception. 188 } 189 } 190 191 /** 192 * Close the region without using ZK 193 */ 194 private void closeRegionNoZK() throws Exception { 195 // no transition in ZK 196 AdminProtos.CloseRegionRequest crr = 197 ProtobufUtil.buildCloseRegionRequest(getRS().getServerName(), regionName); 198 AdminProtos.CloseRegionResponse responseClose = getRS().getRpcServices().closeRegion(null, crr); 199 Assert.assertTrue(responseClose.getClosed()); 200 201 // now waiting & checking. After a while, the transition should be done and the region closed 202 checkRegionIsClosed(HTU, getRS(), hri); 203 } 204 205 @Test 206 public void testCloseByRegionServer() throws Exception { 207 closeRegionNoZK(); 208 openRegion(HTU, getRS(), hri); 209 } 210 211 @Test 212 public void testMultipleCloseFromMaster() throws Exception { 213 for (int i = 0; i < 10; i++) { 214 AdminProtos.CloseRegionRequest crr = 215 ProtobufUtil.buildCloseRegionRequest(getRS().getServerName(), regionName, null); 216 try { 217 AdminProtos.CloseRegionResponse responseClose = 218 getRS().getRpcServices().closeRegion(null, crr); 219 Assert.assertTrue("request " + i + " failed", 220 responseClose.getClosed() || responseClose.hasClosed()); 221 } catch (org.apache.hbase.thirdparty.com.google.protobuf.ServiceException se) { 222 Assert.assertTrue("The next queries may throw an exception.", i > 0); 223 } 224 } 225 226 checkRegionIsClosed(HTU, getRS(), hri); 227 228 openRegion(HTU, getRS(), hri); 229 } 230 231 /** 232 * Test that if we do a close while opening it stops the opening. 233 */ 234 @Test 235 public void testCancelOpeningWithoutZK() throws Exception { 236 // We close 237 closeRegionNoZK(); 238 checkRegionIsClosed(HTU, getRS(), hri); 239 240 // Let do the initial steps, without having a handler 241 getRS().getRegionsInTransitionInRS().put(hri.getEncodedNameAsBytes(), Boolean.TRUE); 242 243 // That's a close without ZK. 244 AdminProtos.CloseRegionRequest crr = 245 ProtobufUtil.buildCloseRegionRequest(getRS().getServerName(), regionName); 246 try { 247 getRS().getRpcServices().closeRegion(null, crr); 248 Assert.assertTrue(false); 249 } catch (org.apache.hbase.thirdparty.com.google.protobuf.ServiceException expected) { 250 } 251 252 // The state in RIT should have changed to close 253 Assert.assertEquals(Boolean.FALSE, 254 getRS().getRegionsInTransitionInRS().get(hri.getEncodedNameAsBytes())); 255 256 // Let's start the open handler 257 TableDescriptor htd = getRS().getTableDescriptors().get(hri.getTable()); 258 259 getRS().getExecutorService().submit(new OpenRegionHandler(getRS(), getRS(), hri, htd, -1)); 260 261 // The open handler should have removed the region from RIT but kept the region closed 262 checkRegionIsClosed(HTU, getRS(), hri); 263 264 openRegion(HTU, getRS(), hri); 265 } 266 267 /** 268 * Tests an on-the-fly RPC that was scheduled for the earlier RS on the same port for openRegion. 269 * The region server should reject this RPC. (HBASE-9721) 270 */ 271 @Test 272 public void testOpenCloseRegionRPCIntendedForPreviousServer() throws Exception { 273 Assert.assertTrue(getRS().getRegion(regionName).isAvailable()); 274 275 ServerName sn = getRS().getServerName(); 276 ServerName earlierServerName = ServerName.valueOf(sn.getHostname(), sn.getPort(), 1); 277 278 try { 279 CloseRegionRequest request = 280 ProtobufUtil.buildCloseRegionRequest(earlierServerName, regionName); 281 getRS().getRSRpcServices().closeRegion(null, request); 282 Assert.fail("The closeRegion should have been rejected"); 283 } catch (org.apache.hbase.thirdparty.com.google.protobuf.ServiceException se) { 284 Assert.assertTrue(se.getCause() instanceof IOException); 285 Assert.assertTrue( 286 se.getCause().getMessage().contains("This RPC was intended for a different server")); 287 } 288 289 // actual close 290 closeRegionNoZK(); 291 try { 292 AdminProtos.OpenRegionRequest orr = 293 RequestConverter.buildOpenRegionRequest(earlierServerName, hri, null); 294 getRS().getRSRpcServices().openRegion(null, orr); 295 Assert.fail("The openRegion should have been rejected"); 296 } catch (org.apache.hbase.thirdparty.com.google.protobuf.ServiceException se) { 297 Assert.assertTrue(se.getCause() instanceof IOException); 298 Assert.assertTrue( 299 se.getCause().getMessage().contains("This RPC was intended for a different server")); 300 } finally { 301 openRegion(HTU, getRS(), hri); 302 } 303 } 304 305 @Test 306 public void testInstallShutdownHook() throws IOException { 307 // Test for HBASE-26951 308 assertTrue(HTU.getHBaseCluster().getRegionServer(0).isShutdownHookInstalled()); 309 } 310}