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.client.Put; 034import org.apache.hadoop.hbase.client.RegionStatesCount; 035import org.apache.hadoop.hbase.client.Table; 036import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; 037import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor; 038import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 039import org.apache.hadoop.hbase.coprocessor.MasterObserver; 040import org.apache.hadoop.hbase.coprocessor.ObserverContext; 041import org.apache.hadoop.hbase.master.HMaster; 042import org.apache.hadoop.hbase.regionserver.HRegionServer; 043import org.apache.hadoop.hbase.testclassification.SmallTests; 044import org.apache.hadoop.hbase.util.Bytes; 045import org.apache.hadoop.hbase.util.JVMClusterUtil.MasterThread; 046import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread; 047import org.junit.AfterClass; 048import org.junit.Assert; 049import org.junit.BeforeClass; 050import org.junit.ClassRule; 051import org.junit.Test; 052import org.junit.experimental.categories.Category; 053 054@Category(SmallTests.class) 055public class TestClientClusterMetrics { 056 057 @ClassRule 058 public static final HBaseClassTestRule CLASS_RULE = 059 HBaseClassTestRule.forClass(TestClientClusterMetrics.class); 060 061 private static HBaseTestingUtility UTIL; 062 private static Admin ADMIN; 063 private final static int SLAVES = 5; 064 private final static int MASTERS = 3; 065 private static MiniHBaseCluster CLUSTER; 066 private static HRegionServer DEAD; 067 private static final TableName TABLE_NAME = TableName.valueOf("test"); 068 private static final byte[] CF = Bytes.toBytes("cf"); 069 070 071 @BeforeClass 072 public static void setUpBeforeClass() throws Exception { 073 Configuration conf = HBaseConfiguration.create(); 074 conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, MyObserver.class.getName()); 075 UTIL = new HBaseTestingUtility(conf); 076 UTIL.startMiniCluster(MASTERS, SLAVES); 077 CLUSTER = UTIL.getHBaseCluster(); 078 CLUSTER.waitForActiveAndReadyMaster(); 079 ADMIN = UTIL.getAdmin(); 080 // Kill one region server 081 List<RegionServerThread> rsts = CLUSTER.getLiveRegionServerThreads(); 082 RegionServerThread rst = rsts.get(rsts.size() - 1); 083 DEAD = rst.getRegionServer(); 084 DEAD.stop("Test dead servers metrics"); 085 while (rst.isAlive()) { 086 Thread.sleep(500); 087 } 088 } 089 090 @Test 091 public void testDefaults() throws Exception { 092 ClusterMetrics origin = ADMIN.getClusterMetrics(); 093 ClusterMetrics defaults = ADMIN.getClusterMetrics(EnumSet.allOf(Option.class)); 094 Assert.assertEquals(origin.getHBaseVersion(), defaults.getHBaseVersion()); 095 Assert.assertEquals(origin.getClusterId(), defaults.getClusterId()); 096 Assert.assertEquals(origin.getAverageLoad(), defaults.getAverageLoad(), 0); 097 Assert.assertEquals(origin.getBackupMasterNames().size(), 098 defaults.getBackupMasterNames().size()); 099 Assert.assertEquals(origin.getDeadServerNames().size(), defaults.getDeadServerNames().size()); 100 Assert.assertEquals(origin.getRegionCount(), defaults.getRegionCount()); 101 Assert.assertEquals(origin.getLiveServerMetrics().size(), 102 defaults.getLiveServerMetrics().size()); 103 Assert.assertEquals(origin.getMasterInfoPort(), defaults.getMasterInfoPort()); 104 } 105 106 @Test 107 public void testAsyncClient() throws Exception { 108 try (AsyncConnection asyncConnect = ConnectionFactory.createAsyncConnection( 109 UTIL.getConfiguration()).get()) { 110 AsyncAdmin asyncAdmin = asyncConnect.getAdmin(); 111 CompletableFuture<ClusterMetrics> originFuture = 112 asyncAdmin.getClusterMetrics(); 113 CompletableFuture<ClusterMetrics> defaultsFuture = 114 asyncAdmin.getClusterMetrics(EnumSet.allOf(Option.class)); 115 ClusterMetrics origin = originFuture.get(); 116 ClusterMetrics defaults = defaultsFuture.get(); 117 Assert.assertEquals(origin.getHBaseVersion(), defaults.getHBaseVersion()); 118 Assert.assertEquals(origin.getClusterId(), defaults.getClusterId()); 119 Assert.assertEquals(origin.getHBaseVersion(), defaults.getHBaseVersion()); 120 Assert.assertEquals(origin.getClusterId(), defaults.getClusterId()); 121 Assert.assertEquals(origin.getAverageLoad(), defaults.getAverageLoad(), 0); 122 Assert.assertEquals(origin.getBackupMasterNames().size(), 123 defaults.getBackupMasterNames().size()); 124 Assert.assertEquals(origin.getDeadServerNames().size(), defaults.getDeadServerNames().size()); 125 Assert.assertEquals(origin.getRegionCount(), defaults.getRegionCount()); 126 Assert.assertEquals(origin.getLiveServerMetrics().size(), 127 defaults.getLiveServerMetrics().size()); 128 Assert.assertEquals(origin.getMasterInfoPort(), defaults.getMasterInfoPort()); 129 origin.getTableRegionStatesCount().forEach(((tableName, regionStatesCount) -> { 130 RegionStatesCount defaultRegionStatesCount = defaults.getTableRegionStatesCount() 131 .get(tableName); 132 Assert.assertEquals(defaultRegionStatesCount, regionStatesCount); 133 })); 134 } 135 } 136 137 @Test 138 public void testLiveAndDeadServersStatus() throws Exception { 139 // Count the number of live regionservers 140 List<RegionServerThread> regionserverThreads = CLUSTER.getLiveRegionServerThreads(); 141 int numRs = 0; 142 int len = regionserverThreads.size(); 143 for (int i = 0; i < len; i++) { 144 if (regionserverThreads.get(i).isAlive()) { 145 numRs++; 146 } 147 } 148 // Depending on the (random) order of unit execution we may run this unit before the 149 // minicluster is fully up and recovered from the RS shutdown done during test init. 150 Waiter.waitFor(CLUSTER.getConfiguration(), 10 * 1000, 100, new Predicate<Exception>() { 151 @Override 152 public boolean evaluate() throws Exception { 153 ClusterMetrics metrics = ADMIN.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)); 154 Assert.assertNotNull(metrics); 155 return metrics.getRegionCount() > 0; 156 } 157 }); 158 // Retrieve live servers and dead servers info. 159 EnumSet<Option> options = EnumSet.of(Option.LIVE_SERVERS, Option.DEAD_SERVERS); 160 ClusterMetrics metrics = ADMIN.getClusterMetrics(options); 161 Assert.assertNotNull(metrics); 162 // exclude a dead region server 163 Assert.assertEquals(SLAVES -1, numRs); 164 // live servers = nums of regionservers 165 // By default, HMaster don't carry any regions so it won't report its load. 166 // Hence, it won't be in the server list. 167 Assert.assertEquals(numRs, metrics.getLiveServerMetrics().size()); 168 Assert.assertTrue(metrics.getRegionCount() > 0); 169 Assert.assertNotNull(metrics.getDeadServerNames()); 170 Assert.assertEquals(1, metrics.getDeadServerNames().size()); 171 ServerName deadServerName = metrics.getDeadServerNames().iterator().next(); 172 Assert.assertEquals(DEAD.getServerName(), deadServerName); 173 } 174 175 @Test 176 public void testRegionStatesCount() throws Exception { 177 Table table = UTIL.createTable(TABLE_NAME, CF); 178 table.put(new Put(Bytes.toBytes("k1")) 179 .addColumn(CF, Bytes.toBytes("q1"), Bytes.toBytes("v1"))); 180 table.put(new Put(Bytes.toBytes("k2")) 181 .addColumn(CF, Bytes.toBytes("q2"), Bytes.toBytes("v2"))); 182 table.put(new Put(Bytes.toBytes("k3")) 183 .addColumn(CF, Bytes.toBytes("q3"), Bytes.toBytes("v3"))); 184 185 ClusterMetrics metrics = ADMIN.getClusterMetrics(); 186 Assert.assertEquals(metrics.getTableRegionStatesCount().size(), 3); 187 Assert.assertEquals(metrics.getTableRegionStatesCount().get(TableName.META_TABLE_NAME) 188 .getRegionsInTransition(), 0); 189 Assert.assertEquals(metrics.getTableRegionStatesCount().get(TableName.META_TABLE_NAME) 190 .getOpenRegions(), 1); 191 Assert.assertEquals(metrics.getTableRegionStatesCount().get(TableName.META_TABLE_NAME) 192 .getTotalRegions(), 1); 193 Assert.assertEquals(metrics.getTableRegionStatesCount().get(TableName.META_TABLE_NAME) 194 .getClosedRegions(), 0); 195 Assert.assertEquals(metrics.getTableRegionStatesCount().get(TableName.META_TABLE_NAME) 196 .getSplitRegions(), 0); 197 Assert.assertEquals(metrics.getTableRegionStatesCount().get(TABLE_NAME) 198 .getRegionsInTransition(), 0); 199 Assert.assertEquals(metrics.getTableRegionStatesCount().get(TABLE_NAME) 200 .getOpenRegions(), 1); 201 Assert.assertEquals(metrics.getTableRegionStatesCount().get(TABLE_NAME) 202 .getTotalRegions(), 1); 203 204 UTIL.deleteTable(TABLE_NAME); 205 } 206 207 @Test 208 public void testMasterAndBackupMastersStatus() throws Exception { 209 // get all the master threads 210 List<MasterThread> masterThreads = CLUSTER.getMasterThreads(); 211 int numActive = 0; 212 int activeIndex = 0; 213 ServerName activeName = null; 214 HMaster active = null; 215 for (int i = 0; i < masterThreads.size(); i++) { 216 if (masterThreads.get(i).getMaster().isActiveMaster()) { 217 numActive++; 218 activeIndex = i; 219 active = masterThreads.get(activeIndex).getMaster(); 220 activeName = active.getServerName(); 221 } 222 } 223 Assert.assertNotNull(active); 224 Assert.assertEquals(1, numActive); 225 Assert.assertEquals(MASTERS, masterThreads.size()); 226 // Retrieve master and backup masters infos only. 227 EnumSet<Option> options = EnumSet.of(Option.MASTER, Option.BACKUP_MASTERS); 228 ClusterMetrics metrics = ADMIN.getClusterMetrics(options); 229 Assert.assertTrue(metrics.getMasterName().equals(activeName)); 230 Assert.assertEquals(MASTERS - 1, metrics.getBackupMasterNames().size()); 231 } 232 233 @Test 234 public void testOtherStatusInfos() throws Exception { 235 EnumSet<Option> options = 236 EnumSet.of(Option.MASTER_COPROCESSORS, Option.HBASE_VERSION, 237 Option.CLUSTER_ID, Option.BALANCER_ON); 238 ClusterMetrics metrics = ADMIN.getClusterMetrics(options); 239 Assert.assertEquals(1, metrics.getMasterCoprocessorNames().size()); 240 Assert.assertNotNull(metrics.getHBaseVersion()); 241 Assert.assertNotNull(metrics.getClusterId()); 242 Assert.assertTrue(metrics.getAverageLoad() == 0.0); 243 Assert.assertNotNull(metrics.getBalancerOn()); 244 } 245 246 @AfterClass 247 public static void tearDownAfterClass() throws Exception { 248 if (ADMIN != null) { 249 ADMIN.close(); 250 } 251 UTIL.shutdownMiniCluster(); 252 } 253 254 @Test 255 public void testObserver() throws IOException { 256 int preCount = MyObserver.PRE_COUNT.get(); 257 int postCount = MyObserver.POST_COUNT.get(); 258 Assert.assertTrue(ADMIN.getClusterMetrics().getMasterCoprocessorNames().stream() 259 .anyMatch(s -> s.equals(MyObserver.class.getSimpleName()))); 260 Assert.assertEquals(preCount + 1, MyObserver.PRE_COUNT.get()); 261 Assert.assertEquals(postCount + 1, MyObserver.POST_COUNT.get()); 262 } 263 264 public static class MyObserver implements MasterCoprocessor, MasterObserver { 265 private static final AtomicInteger PRE_COUNT = new AtomicInteger(0); 266 private static final AtomicInteger POST_COUNT = new AtomicInteger(0); 267 268 @Override public Optional<MasterObserver> getMasterObserver() { 269 return Optional.of(this); 270 } 271 272 @Override public void preGetClusterMetrics(ObserverContext<MasterCoprocessorEnvironment> ctx) 273 throws IOException { 274 PRE_COUNT.incrementAndGet(); 275 } 276 277 @Override public void postGetClusterMetrics(ObserverContext<MasterCoprocessorEnvironment> ctx, 278 ClusterMetrics metrics) throws IOException { 279 POST_COUNT.incrementAndGet(); 280 } 281 } 282}