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