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