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 StartMiniClusterOption option = StartMiniClusterOption.builder() 077 .numMasters(MASTERS).numRegionServers(SLAVES).numDataNodes(SLAVES).build(); 078 UTIL.startMiniCluster(option); 079 CLUSTER = UTIL.getHBaseCluster(); 080 CLUSTER.waitForActiveAndReadyMaster(); 081 ADMIN = UTIL.getAdmin(); 082 // Kill one region server 083 List<RegionServerThread> rsts = CLUSTER.getLiveRegionServerThreads(); 084 RegionServerThread rst = rsts.get(rsts.size() - 1); 085 DEAD = rst.getRegionServer(); 086 DEAD.stop("Test dead servers metrics"); 087 while (rst.isAlive()) { 088 Thread.sleep(500); 089 } 090 } 091 092 @Test 093 public void testDefaults() throws Exception { 094 ClusterMetrics origin = ADMIN.getClusterMetrics(); 095 ClusterMetrics defaults = ADMIN.getClusterMetrics(EnumSet.allOf(Option.class)); 096 Assert.assertEquals(origin.getHBaseVersion(), defaults.getHBaseVersion()); 097 Assert.assertEquals(origin.getClusterId(), defaults.getClusterId()); 098 Assert.assertEquals(origin.getAverageLoad(), defaults.getAverageLoad(), 0); 099 Assert.assertEquals(origin.getBackupMasterNames().size(), 100 defaults.getBackupMasterNames().size()); 101 Assert.assertEquals(origin.getDeadServerNames().size(), defaults.getDeadServerNames().size()); 102 Assert.assertEquals(origin.getRegionCount(), defaults.getRegionCount()); 103 Assert.assertEquals(origin.getLiveServerMetrics().size(), 104 defaults.getLiveServerMetrics().size()); 105 Assert.assertEquals(origin.getMasterInfoPort(), defaults.getMasterInfoPort()); 106 Assert.assertEquals(origin.getServersName().size(), defaults.getServersName().size()); 107 Assert.assertEquals(ADMIN.getRegionServers().size(), defaults.getServersName().size()); 108 } 109 110 @Test 111 public void testAsyncClient() throws Exception { 112 try (AsyncConnection asyncConnect = ConnectionFactory.createAsyncConnection( 113 UTIL.getConfiguration()).get()) { 114 AsyncAdmin asyncAdmin = asyncConnect.getAdmin(); 115 CompletableFuture<ClusterMetrics> originFuture = 116 asyncAdmin.getClusterMetrics(); 117 CompletableFuture<ClusterMetrics> defaultsFuture = 118 asyncAdmin.getClusterMetrics(EnumSet.allOf(Option.class)); 119 ClusterMetrics origin = originFuture.get(); 120 ClusterMetrics defaults = defaultsFuture.get(); 121 Assert.assertEquals(origin.getHBaseVersion(), defaults.getHBaseVersion()); 122 Assert.assertEquals(origin.getClusterId(), defaults.getClusterId()); 123 Assert.assertEquals(origin.getHBaseVersion(), defaults.getHBaseVersion()); 124 Assert.assertEquals(origin.getClusterId(), defaults.getClusterId()); 125 Assert.assertEquals(origin.getAverageLoad(), defaults.getAverageLoad(), 0); 126 Assert.assertEquals(origin.getBackupMasterNames().size(), 127 defaults.getBackupMasterNames().size()); 128 Assert.assertEquals(origin.getDeadServerNames().size(), defaults.getDeadServerNames().size()); 129 Assert.assertEquals(origin.getRegionCount(), defaults.getRegionCount()); 130 Assert.assertEquals(origin.getLiveServerMetrics().size(), 131 defaults.getLiveServerMetrics().size()); 132 Assert.assertEquals(origin.getMasterInfoPort(), defaults.getMasterInfoPort()); 133 Assert.assertEquals(origin.getServersName().size(), defaults.getServersName().size()); 134 origin.getTableRegionStatesCount().forEach(((tableName, regionStatesCount) -> { 135 RegionStatesCount defaultRegionStatesCount = defaults.getTableRegionStatesCount() 136 .get(tableName); 137 Assert.assertEquals(defaultRegionStatesCount, regionStatesCount); 138 })); 139 } 140 } 141 142 @Test 143 public void testLiveAndDeadServersStatus() throws Exception { 144 // Count the number of live regionservers 145 List<RegionServerThread> regionserverThreads = CLUSTER.getLiveRegionServerThreads(); 146 int numRs = 0; 147 int len = regionserverThreads.size(); 148 for (int i = 0; i < len; i++) { 149 if (regionserverThreads.get(i).isAlive()) { 150 numRs++; 151 } 152 } 153 // Depending on the (random) order of unit execution we may run this unit before the 154 // minicluster is fully up and recovered from the RS shutdown done during test init. 155 Waiter.waitFor(CLUSTER.getConfiguration(), 10 * 1000, 100, new Predicate<Exception>() { 156 @Override 157 public boolean evaluate() throws Exception { 158 ClusterMetrics metrics = ADMIN.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)); 159 Assert.assertNotNull(metrics); 160 return metrics.getRegionCount() > 0; 161 } 162 }); 163 // Retrieve live servers and dead servers info. 164 EnumSet<Option> options = 165 EnumSet.of(Option.LIVE_SERVERS, Option.DEAD_SERVERS, Option.SERVERS_NAME); 166 ClusterMetrics metrics = ADMIN.getClusterMetrics(options); 167 Assert.assertNotNull(metrics); 168 // exclude a dead region server 169 Assert.assertEquals(SLAVES -1, numRs); 170 // live servers = nums of regionservers 171 // By default, HMaster don't carry any regions so it won't report its load. 172 // Hence, it won't be in the server list. 173 Assert.assertEquals(numRs, metrics.getLiveServerMetrics().size()); 174 Assert.assertTrue(metrics.getRegionCount() > 0); 175 Assert.assertNotNull(metrics.getDeadServerNames()); 176 Assert.assertEquals(1, metrics.getDeadServerNames().size()); 177 ServerName deadServerName = metrics.getDeadServerNames().iterator().next(); 178 Assert.assertEquals(DEAD.getServerName(), deadServerName); 179 Assert.assertNotNull(metrics.getServersName()); 180 Assert.assertEquals(numRs, metrics.getServersName().size()); 181 } 182 183 @Test 184 public void testRegionStatesCount() throws Exception { 185 Table table = UTIL.createTable(TABLE_NAME, CF); 186 table.put(new Put(Bytes.toBytes("k1")) 187 .addColumn(CF, Bytes.toBytes("q1"), Bytes.toBytes("v1"))); 188 table.put(new Put(Bytes.toBytes("k2")) 189 .addColumn(CF, Bytes.toBytes("q2"), Bytes.toBytes("v2"))); 190 table.put(new Put(Bytes.toBytes("k3")) 191 .addColumn(CF, Bytes.toBytes("q3"), Bytes.toBytes("v3"))); 192 193 ClusterMetrics metrics = ADMIN.getClusterMetrics(); 194 Assert.assertEquals(metrics.getTableRegionStatesCount().size(), 3); 195 Assert.assertEquals(metrics.getTableRegionStatesCount().get(TableName.META_TABLE_NAME) 196 .getRegionsInTransition(), 0); 197 Assert.assertEquals(metrics.getTableRegionStatesCount().get(TableName.META_TABLE_NAME) 198 .getOpenRegions(), 1); 199 Assert.assertEquals(metrics.getTableRegionStatesCount().get(TableName.META_TABLE_NAME) 200 .getTotalRegions(), 1); 201 Assert.assertEquals(metrics.getTableRegionStatesCount().get(TableName.META_TABLE_NAME) 202 .getClosedRegions(), 0); 203 Assert.assertEquals(metrics.getTableRegionStatesCount().get(TableName.META_TABLE_NAME) 204 .getSplitRegions(), 0); 205 Assert.assertEquals(metrics.getTableRegionStatesCount().get(TABLE_NAME) 206 .getRegionsInTransition(), 0); 207 Assert.assertEquals(metrics.getTableRegionStatesCount().get(TABLE_NAME) 208 .getOpenRegions(), 1); 209 Assert.assertEquals(metrics.getTableRegionStatesCount().get(TABLE_NAME) 210 .getTotalRegions(), 1); 211 212 UTIL.deleteTable(TABLE_NAME); 213 } 214 215 @Test 216 public void testMasterAndBackupMastersStatus() throws Exception { 217 // get all the master threads 218 List<MasterThread> masterThreads = CLUSTER.getMasterThreads(); 219 int numActive = 0; 220 int activeIndex = 0; 221 ServerName activeName = null; 222 HMaster active = null; 223 for (int i = 0; i < masterThreads.size(); i++) { 224 if (masterThreads.get(i).getMaster().isActiveMaster()) { 225 numActive++; 226 activeIndex = i; 227 active = masterThreads.get(activeIndex).getMaster(); 228 activeName = active.getServerName(); 229 } 230 } 231 Assert.assertNotNull(active); 232 Assert.assertEquals(1, numActive); 233 Assert.assertEquals(MASTERS, masterThreads.size()); 234 // Retrieve master and backup masters infos only. 235 EnumSet<Option> options = EnumSet.of(Option.MASTER, Option.BACKUP_MASTERS); 236 ClusterMetrics metrics = ADMIN.getClusterMetrics(options); 237 Assert.assertTrue(metrics.getMasterName().equals(activeName)); 238 Assert.assertEquals(MASTERS - 1, metrics.getBackupMasterNames().size()); 239 } 240 241 @Test 242 public void testOtherStatusInfos() throws Exception { 243 EnumSet<Option> options = 244 EnumSet.of(Option.MASTER_COPROCESSORS, Option.HBASE_VERSION, 245 Option.CLUSTER_ID, Option.BALANCER_ON); 246 ClusterMetrics metrics = ADMIN.getClusterMetrics(options); 247 Assert.assertEquals(1, metrics.getMasterCoprocessorNames().size()); 248 Assert.assertNotNull(metrics.getHBaseVersion()); 249 Assert.assertNotNull(metrics.getClusterId()); 250 Assert.assertTrue(metrics.getAverageLoad() == 0.0); 251 Assert.assertNotNull(metrics.getBalancerOn()); 252 } 253 254 @AfterClass 255 public static void tearDownAfterClass() throws Exception { 256 if (ADMIN != null) { 257 ADMIN.close(); 258 } 259 UTIL.shutdownMiniCluster(); 260 } 261 262 @Test 263 public void testObserver() throws IOException { 264 int preCount = MyObserver.PRE_COUNT.get(); 265 int postCount = MyObserver.POST_COUNT.get(); 266 Assert.assertTrue(ADMIN.getClusterMetrics().getMasterCoprocessorNames().stream() 267 .anyMatch(s -> s.equals(MyObserver.class.getSimpleName()))); 268 Assert.assertEquals(preCount + 1, MyObserver.PRE_COUNT.get()); 269 Assert.assertEquals(postCount + 1, MyObserver.POST_COUNT.get()); 270 } 271 272 private static void insertData(final TableName tableName, int startRow, int rowCount) 273 throws IOException { 274 Table t = UTIL.getConnection().getTable(tableName); 275 Put p; 276 for (int i = 0; i < rowCount; i++) { 277 p = new Put(Bytes.toBytes("" + (startRow + i))); 278 p.addColumn(CF, Bytes.toBytes("val1"), Bytes.toBytes(i)); 279 t.put(p); 280 } 281 } 282 283 public static class MyObserver implements MasterCoprocessor, MasterObserver { 284 private static final AtomicInteger PRE_COUNT = new AtomicInteger(0); 285 private static final AtomicInteger POST_COUNT = new AtomicInteger(0); 286 287 @Override public Optional<MasterObserver> getMasterObserver() { 288 return Optional.of(this); 289 } 290 291 @Override public void preGetClusterMetrics(ObserverContext<MasterCoprocessorEnvironment> ctx) 292 throws IOException { 293 PRE_COUNT.incrementAndGet(); 294 } 295 296 @Override public void postGetClusterMetrics(ObserverContext<MasterCoprocessorEnvironment> ctx, 297 ClusterMetrics metrics) throws IOException { 298 POST_COUNT.incrementAndGet(); 299 } 300 } 301}