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; 019 020import java.io.IOException; 021import java.util.EnumSet; 022import java.util.List; 023import java.util.Optional; 024import java.util.concurrent.atomic.AtomicInteger; 025import java.util.stream.Stream; 026import org.apache.hadoop.conf.Configuration; 027import org.apache.hadoop.hbase.ClusterMetrics.Option; 028import org.apache.hadoop.hbase.Waiter.Predicate; 029import org.apache.hadoop.hbase.client.Admin; 030import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; 031import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor; 032import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 033import org.apache.hadoop.hbase.coprocessor.MasterObserver; 034import org.apache.hadoop.hbase.coprocessor.ObserverContext; 035import org.apache.hadoop.hbase.master.HMaster; 036import org.apache.hadoop.hbase.regionserver.HRegionServer; 037import org.apache.hadoop.hbase.testclassification.MediumTests; 038import org.apache.hadoop.hbase.util.JVMClusterUtil.MasterThread; 039import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread; 040import org.junit.AfterClass; 041import org.junit.Assert; 042import org.junit.BeforeClass; 043import org.junit.ClassRule; 044import org.junit.Test; 045import org.junit.experimental.categories.Category; 046 047/** 048 * Test the ClusterStatus. 049 */ 050@Category(MediumTests.class) 051public class TestClientClusterStatus { 052 053 @ClassRule 054 public static final HBaseClassTestRule CLASS_RULE = 055 HBaseClassTestRule.forClass(TestClientClusterStatus.class); 056 057 private static HBaseTestingUtility UTIL; 058 private static Admin ADMIN; 059 private final static int SLAVES = 5; 060 private final static int MASTERS = 3; 061 private static MiniHBaseCluster CLUSTER; 062 private static HRegionServer DEAD; 063 064 @BeforeClass 065 public static void setUpBeforeClass() throws Exception { 066 Configuration conf = HBaseConfiguration.create(); 067 conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, MyObserver.class.getName()); 068 UTIL = new HBaseTestingUtility(conf); 069 StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(MASTERS) 070 .numRegionServers(SLAVES).numDataNodes(SLAVES).build(); 071 UTIL.startMiniCluster(option); 072 CLUSTER = UTIL.getHBaseCluster(); 073 CLUSTER.waitForActiveAndReadyMaster(); 074 ADMIN = UTIL.getAdmin(); 075 // Kill one region server 076 List<RegionServerThread> rsts = CLUSTER.getLiveRegionServerThreads(); 077 RegionServerThread rst = rsts.get(rsts.size() - 1); 078 DEAD = rst.getRegionServer(); 079 DEAD.stop("Test dead servers status"); 080 while (rst.isAlive()) { 081 Thread.sleep(500); 082 } 083 } 084 085 @Test 086 public void testDefaults() throws Exception { 087 ClusterStatus origin = ADMIN.getClusterStatus(); 088 ClusterStatus defaults = 089 new ClusterStatus(ADMIN.getClusterMetrics(EnumSet.allOf(Option.class))); 090 checkPbObjectNotNull(origin); 091 checkPbObjectNotNull(defaults); 092 Assert.assertEquals(origin.getHBaseVersion(), defaults.getHBaseVersion()); 093 Assert.assertEquals(origin.getClusterId(), defaults.getClusterId()); 094 Assert.assertTrue(origin.getAverageLoad() == defaults.getAverageLoad()); 095 Assert.assertTrue(origin.getBackupMastersSize() == defaults.getBackupMastersSize()); 096 Assert.assertTrue(origin.getDeadServersSize() == defaults.getDeadServersSize()); 097 Assert.assertTrue(origin.getRegionsCount() == defaults.getRegionsCount()); 098 Assert.assertTrue(origin.getServersSize() == defaults.getServersSize()); 099 Assert.assertTrue(origin.getMasterInfoPort() == defaults.getMasterInfoPort()); 100 Assert.assertTrue(origin.equals(defaults)); 101 Assert.assertTrue(origin.getServersName().size() == defaults.getServersName().size()); 102 } 103 104 @Test 105 public void testNone() throws Exception { 106 ClusterMetrics status0 = ADMIN.getClusterMetrics(EnumSet.allOf(Option.class)); 107 ClusterMetrics status1 = ADMIN.getClusterMetrics(EnumSet.noneOf(Option.class)); 108 // Do a rough compare. More specific compares can fail because all regions not deployed yet 109 // or more requests than expected. 110 Assert.assertEquals(status0.getLiveServerMetrics().size(), 111 status1.getLiveServerMetrics().size()); 112 checkPbObjectNotNull(new ClusterStatus(status0)); 113 checkPbObjectNotNull(new ClusterStatus(status1)); 114 } 115 116 @Test 117 public void testLiveAndDeadServersStatus() throws Exception { 118 // Count the number of live regionservers 119 List<RegionServerThread> regionserverThreads = CLUSTER.getLiveRegionServerThreads(); 120 int numRs = 0; 121 int len = regionserverThreads.size(); 122 for (int i = 0; i < len; i++) { 123 if (regionserverThreads.get(i).isAlive()) { 124 numRs++; 125 } 126 } 127 // Depending on the (random) order of unit execution we may run this unit before the 128 // minicluster is fully up and recovered from the RS shutdown done during test init. 129 Waiter.waitFor(CLUSTER.getConfiguration(), 10 * 1000, 100, new Predicate<Exception>() { 130 @Override 131 public boolean evaluate() throws Exception { 132 ClusterStatus status = 133 new ClusterStatus(ADMIN.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))); 134 Assert.assertNotNull(status); 135 return status.getRegionsCount() > 0; 136 } 137 }); 138 // Retrieve live servers and dead servers info. 139 EnumSet<Option> options = 140 EnumSet.of(Option.LIVE_SERVERS, Option.DEAD_SERVERS, Option.SERVERS_NAME); 141 ClusterStatus status = new ClusterStatus(ADMIN.getClusterMetrics(options)); 142 checkPbObjectNotNull(status); 143 Assert.assertNotNull(status); 144 Assert.assertNotNull(status.getServers()); 145 // exclude a dead region server 146 Assert.assertEquals(SLAVES - 1, numRs); 147 // live servers = nums of regionservers 148 // By default, HMaster don't carry any regions so it won't report its load. 149 // Hence, it won't be in the server list. 150 Assert.assertEquals(status.getServers().size(), numRs); 151 Assert.assertTrue(status.getRegionsCount() > 0); 152 Assert.assertNotNull(status.getDeadServerNames()); 153 Assert.assertEquals(1, status.getDeadServersSize()); 154 ServerName deadServerName = status.getDeadServerNames().iterator().next(); 155 Assert.assertEquals(DEAD.getServerName(), deadServerName); 156 Assert.assertNotNull(status.getServersName()); 157 Assert.assertEquals(numRs, status.getServersName().size()); 158 } 159 160 @Test 161 public void testMasterAndBackupMastersStatus() throws Exception { 162 // get all the master threads 163 List<MasterThread> masterThreads = CLUSTER.getMasterThreads(); 164 int numActive = 0; 165 int activeIndex = 0; 166 ServerName activeName = null; 167 HMaster active = null; 168 for (int i = 0; i < masterThreads.size(); i++) { 169 if (masterThreads.get(i).getMaster().isActiveMaster()) { 170 numActive++; 171 activeIndex = i; 172 active = masterThreads.get(activeIndex).getMaster(); 173 activeName = active.getServerName(); 174 } 175 } 176 Assert.assertNotNull(active); 177 Assert.assertEquals(1, numActive); 178 Assert.assertEquals(MASTERS, masterThreads.size()); 179 // Retrieve master and backup masters infos only. 180 EnumSet<Option> options = EnumSet.of(Option.MASTER, Option.BACKUP_MASTERS); 181 ClusterStatus status = new ClusterStatus(ADMIN.getClusterMetrics(options)); 182 Assert.assertTrue(status.getMaster().equals(activeName)); 183 Assert.assertEquals(MASTERS - 1, status.getBackupMastersSize()); 184 } 185 186 @Test 187 public void testOtherStatusInfos() throws Exception { 188 EnumSet<Option> options = EnumSet.of(Option.MASTER_COPROCESSORS, Option.HBASE_VERSION, 189 Option.CLUSTER_ID, Option.BALANCER_ON); 190 ClusterStatus status = new ClusterStatus(ADMIN.getClusterMetrics(options)); 191 Assert.assertTrue(status.getMasterCoprocessors().length == 1); 192 Assert.assertNotNull(status.getHBaseVersion()); 193 Assert.assertNotNull(status.getClusterId()); 194 Assert.assertTrue(status.getAverageLoad() == 0.0); 195 Assert.assertNotNull(status.getBalancerOn()); 196 } 197 198 @AfterClass 199 public static void tearDownAfterClass() throws Exception { 200 if (ADMIN != null) ADMIN.close(); 201 UTIL.shutdownMiniCluster(); 202 } 203 204 @Test 205 public void testObserver() throws IOException { 206 int preCount = MyObserver.PRE_COUNT.get(); 207 int postCount = MyObserver.POST_COUNT.get(); 208 Assert.assertTrue(Stream.of(ADMIN.getClusterStatus().getMasterCoprocessors()) 209 .anyMatch(s -> s.equals(MyObserver.class.getSimpleName()))); 210 Assert.assertEquals(preCount + 1, MyObserver.PRE_COUNT.get()); 211 Assert.assertEquals(postCount + 1, MyObserver.POST_COUNT.get()); 212 } 213 214 /** 215 * HBASE-19496 do the refactor for ServerLoad and RegionLoad so the inner pb object is useless 216 * now. However, they are Public classes, and consequently we must make sure the all pb objects 217 * have initialized. 218 */ 219 private static void checkPbObjectNotNull(ClusterStatus status) { 220 for (ServerName name : status.getLiveServerMetrics().keySet()) { 221 ServerLoad load = status.getLoad(name); 222 Assert.assertNotNull(load.obtainServerLoadPB()); 223 for (RegionLoad rl : load.getRegionsLoad().values()) { 224 Assert.assertNotNull(rl.regionLoadPB); 225 } 226 } 227 } 228 229 public static class MyObserver implements MasterCoprocessor, MasterObserver { 230 private static final AtomicInteger PRE_COUNT = new AtomicInteger(0); 231 private static final AtomicInteger POST_COUNT = new AtomicInteger(0); 232 233 @Override 234 public Optional<MasterObserver> getMasterObserver() { 235 return Optional.of(this); 236 } 237 238 @Override 239 public void preGetClusterMetrics(ObserverContext<MasterCoprocessorEnvironment> ctx) 240 throws IOException { 241 PRE_COUNT.incrementAndGet(); 242 } 243 244 @Override 245 public void postGetClusterMetrics(ObserverContext<MasterCoprocessorEnvironment> ctx, 246 ClusterMetrics status) throws IOException { 247 POST_COUNT.incrementAndGet(); 248 } 249 } 250}