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.CompletableFuture; 025import java.util.concurrent.atomic.AtomicInteger; 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.client.AsyncAdmin; 031import org.apache.hadoop.hbase.client.AsyncConnection; 032import org.apache.hadoop.hbase.client.ConnectionFactory; 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.SmallTests; 041import org.apache.hadoop.hbase.util.JVMClusterUtil.MasterThread; 042import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread; 043import org.junit.AfterClass; 044import org.junit.Assert; 045import org.junit.BeforeClass; 046import org.junit.ClassRule; 047import org.junit.Test; 048import org.junit.experimental.categories.Category; 049 050@Category(SmallTests.class) 051public class TestClientClusterMetrics { 052 053 @ClassRule 054 public static final HBaseClassTestRule CLASS_RULE = 055 HBaseClassTestRule.forClass(TestClientClusterMetrics.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 UTIL.startMiniCluster(MASTERS, SLAVES); 070 CLUSTER = UTIL.getHBaseCluster(); 071 CLUSTER.waitForActiveAndReadyMaster(); 072 ADMIN = UTIL.getAdmin(); 073 // Kill one region server 074 List<RegionServerThread> rsts = CLUSTER.getLiveRegionServerThreads(); 075 RegionServerThread rst = rsts.get(rsts.size() - 1); 076 DEAD = rst.getRegionServer(); 077 DEAD.stop("Test dead servers metrics"); 078 while (rst.isAlive()) { 079 Thread.sleep(500); 080 } 081 } 082 083 @Test 084 public void testDefaults() throws Exception { 085 ClusterMetrics origin = ADMIN.getClusterMetrics(); 086 ClusterMetrics defaults = ADMIN.getClusterMetrics(EnumSet.allOf(Option.class)); 087 Assert.assertEquals(origin.getHBaseVersion(), defaults.getHBaseVersion()); 088 Assert.assertEquals(origin.getClusterId(), defaults.getClusterId()); 089 Assert.assertEquals(origin.getAverageLoad(), defaults.getAverageLoad(), 0); 090 Assert.assertEquals(origin.getBackupMasterNames().size(), 091 defaults.getBackupMasterNames().size()); 092 Assert.assertEquals(origin.getDeadServerNames().size(), defaults.getDeadServerNames().size()); 093 Assert.assertEquals(origin.getRegionCount(), defaults.getRegionCount()); 094 Assert.assertEquals(origin.getLiveServerMetrics().size(), 095 defaults.getLiveServerMetrics().size()); 096 Assert.assertEquals(origin.getMasterInfoPort(), defaults.getMasterInfoPort()); 097 } 098 099 @Test 100 public void testAsyncClient() throws Exception { 101 try (AsyncConnection asyncConnect = ConnectionFactory.createAsyncConnection( 102 UTIL.getConfiguration()).get()) { 103 AsyncAdmin asyncAdmin = asyncConnect.getAdmin(); 104 CompletableFuture<ClusterMetrics> originFuture = 105 asyncAdmin.getClusterMetrics(); 106 CompletableFuture<ClusterMetrics> defaultsFuture = 107 asyncAdmin.getClusterMetrics(EnumSet.allOf(Option.class)); 108 ClusterMetrics origin = originFuture.get(); 109 ClusterMetrics defaults = defaultsFuture.get(); 110 Assert.assertEquals(origin.getHBaseVersion(), defaults.getHBaseVersion()); 111 Assert.assertEquals(origin.getClusterId(), defaults.getClusterId()); 112 Assert.assertEquals(origin.getHBaseVersion(), defaults.getHBaseVersion()); 113 Assert.assertEquals(origin.getClusterId(), defaults.getClusterId()); 114 Assert.assertEquals(origin.getAverageLoad(), defaults.getAverageLoad(), 0); 115 Assert.assertEquals(origin.getBackupMasterNames().size(), 116 defaults.getBackupMasterNames().size()); 117 Assert.assertEquals(origin.getDeadServerNames().size(), defaults.getDeadServerNames().size()); 118 Assert.assertEquals(origin.getRegionCount(), defaults.getRegionCount()); 119 Assert.assertEquals(origin.getLiveServerMetrics().size(), 120 defaults.getLiveServerMetrics().size()); 121 Assert.assertEquals(origin.getMasterInfoPort(), defaults.getMasterInfoPort()); 122 } 123 } 124 125 @Test 126 public void testLiveAndDeadServersStatus() throws Exception { 127 // Count the number of live regionservers 128 List<RegionServerThread> regionserverThreads = CLUSTER.getLiveRegionServerThreads(); 129 int numRs = 0; 130 int len = regionserverThreads.size(); 131 for (int i = 0; i < len; i++) { 132 if (regionserverThreads.get(i).isAlive()) { 133 numRs++; 134 } 135 } 136 // Depending on the (random) order of unit execution we may run this unit before the 137 // minicluster is fully up and recovered from the RS shutdown done during test init. 138 Waiter.waitFor(CLUSTER.getConfiguration(), 10 * 1000, 100, new Predicate<Exception>() { 139 @Override 140 public boolean evaluate() throws Exception { 141 ClusterMetrics metrics = ADMIN.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)); 142 Assert.assertNotNull(metrics); 143 return metrics.getRegionCount() > 0; 144 } 145 }); 146 // Retrieve live servers and dead servers info. 147 EnumSet<Option> options = EnumSet.of(Option.LIVE_SERVERS, Option.DEAD_SERVERS); 148 ClusterMetrics metrics = ADMIN.getClusterMetrics(options); 149 Assert.assertNotNull(metrics); 150 // exclude a dead region server 151 Assert.assertEquals(SLAVES -1, numRs); 152 // live servers = nums of regionservers 153 // By default, HMaster don't carry any regions so it won't report its load. 154 // Hence, it won't be in the server list. 155 Assert.assertEquals(numRs, metrics.getLiveServerMetrics().size()); 156 Assert.assertTrue(metrics.getRegionCount() > 0); 157 Assert.assertNotNull(metrics.getDeadServerNames()); 158 Assert.assertEquals(1, metrics.getDeadServerNames().size()); 159 ServerName deadServerName = metrics.getDeadServerNames().iterator().next(); 160 Assert.assertEquals(DEAD.getServerName(), deadServerName); 161 } 162 163 @Test 164 public void testMasterAndBackupMastersStatus() throws Exception { 165 // get all the master threads 166 List<MasterThread> masterThreads = CLUSTER.getMasterThreads(); 167 int numActive = 0; 168 int activeIndex = 0; 169 ServerName activeName = null; 170 HMaster active = null; 171 for (int i = 0; i < masterThreads.size(); i++) { 172 if (masterThreads.get(i).getMaster().isActiveMaster()) { 173 numActive++; 174 activeIndex = i; 175 active = masterThreads.get(activeIndex).getMaster(); 176 activeName = active.getServerName(); 177 } 178 } 179 Assert.assertNotNull(active); 180 Assert.assertEquals(1, numActive); 181 Assert.assertEquals(MASTERS, masterThreads.size()); 182 // Retrieve master and backup masters infos only. 183 EnumSet<Option> options = EnumSet.of(Option.MASTER, Option.BACKUP_MASTERS); 184 ClusterMetrics metrics = ADMIN.getClusterMetrics(options); 185 Assert.assertTrue(metrics.getMasterName().equals(activeName)); 186 Assert.assertEquals(MASTERS - 1, metrics.getBackupMasterNames().size()); 187 } 188 189 @Test 190 public void testOtherStatusInfos() throws Exception { 191 EnumSet<Option> options = 192 EnumSet.of(Option.MASTER_COPROCESSORS, Option.HBASE_VERSION, 193 Option.CLUSTER_ID, Option.BALANCER_ON); 194 ClusterMetrics metrics = ADMIN.getClusterMetrics(options); 195 Assert.assertEquals(1, metrics.getMasterCoprocessorNames().size()); 196 Assert.assertNotNull(metrics.getHBaseVersion()); 197 Assert.assertNotNull(metrics.getClusterId()); 198 Assert.assertTrue(metrics.getAverageLoad() == 0.0); 199 Assert.assertNotNull(metrics.getBalancerOn()); 200 } 201 202 @AfterClass 203 public static void tearDownAfterClass() throws Exception { 204 if (ADMIN != null) { 205 ADMIN.close(); 206 } 207 UTIL.shutdownMiniCluster(); 208 } 209 210 @Test 211 public void testObserver() throws IOException { 212 int preCount = MyObserver.PRE_COUNT.get(); 213 int postCount = MyObserver.POST_COUNT.get(); 214 Assert.assertTrue(ADMIN.getClusterMetrics().getMasterCoprocessorNames().stream() 215 .anyMatch(s -> s.equals(MyObserver.class.getSimpleName()))); 216 Assert.assertEquals(preCount + 1, MyObserver.PRE_COUNT.get()); 217 Assert.assertEquals(postCount + 1, MyObserver.POST_COUNT.get()); 218 } 219 220 public static class MyObserver implements MasterCoprocessor, MasterObserver { 221 private static final AtomicInteger PRE_COUNT = new AtomicInteger(0); 222 private static final AtomicInteger POST_COUNT = new AtomicInteger(0); 223 224 @Override public Optional<MasterObserver> getMasterObserver() { 225 return Optional.of(this); 226 } 227 228 @Override public void preGetClusterMetrics(ObserverContext<MasterCoprocessorEnvironment> ctx) 229 throws IOException { 230 PRE_COUNT.incrementAndGet(); 231 } 232 233 @Override public void postGetClusterMetrics(ObserverContext<MasterCoprocessorEnvironment> ctx, 234 ClusterMetrics metrics) throws IOException { 235 POST_COUNT.incrementAndGet(); 236 } 237 } 238}