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