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.assertTrue; 022 023import java.lang.reflect.InvocationTargetException; 024import java.lang.reflect.Method; 025import java.util.ArrayList; 026import java.util.Collection; 027import java.util.EnumSet; 028import java.util.List; 029import java.util.Map; 030import java.util.concurrent.TimeUnit; 031import java.util.stream.Collectors; 032import org.apache.hadoop.hbase.ClusterMetrics.Option; 033import org.apache.hadoop.hbase.client.Admin; 034import org.apache.hadoop.hbase.client.RegionInfo; 035import org.apache.hadoop.hbase.client.Table; 036import org.apache.hadoop.hbase.testclassification.MediumTests; 037import org.apache.hadoop.hbase.testclassification.MiscTests; 038import org.apache.hadoop.hbase.util.Bytes; 039import org.junit.jupiter.api.AfterAll; 040import org.junit.jupiter.api.BeforeAll; 041import org.junit.jupiter.api.Tag; 042import org.junit.jupiter.api.Test; 043import org.slf4j.Logger; 044import org.slf4j.LoggerFactory; 045 046import org.apache.hbase.thirdparty.com.google.common.collect.Maps; 047 048@Tag(MiscTests.TAG) 049@Tag(MediumTests.TAG) 050public class TestRegionMetrics { 051 052 private static final Logger LOG = LoggerFactory.getLogger(TestRegionMetrics.class); 053 private static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); 054 private static Admin admin; 055 056 private static final TableName TABLE_1 = TableName.valueOf("table_1"); 057 private static final TableName TABLE_2 = TableName.valueOf("table_2"); 058 private static final TableName TABLE_3 = TableName.valueOf("table_3"); 059 private static final TableName[] tables = new TableName[] { TABLE_1, TABLE_2, TABLE_3 }; 060 private static final int MSG_INTERVAL = 500; // ms 061 062 @BeforeAll 063 public static void beforeClass() throws Exception { 064 // Make servers report eagerly. This test is about looking at the cluster status reported. 065 // Make it so we don't have to wait around too long to see change. 066 UTIL.getConfiguration().setInt("hbase.regionserver.msginterval", MSG_INTERVAL); 067 UTIL.startMiniCluster(4); 068 admin = UTIL.getAdmin(); 069 admin.balancerSwitch(false, true); 070 byte[] FAMILY = Bytes.toBytes("f"); 071 for (TableName tableName : tables) { 072 Table table = UTIL.createMultiRegionTable(tableName, FAMILY, 16); 073 UTIL.waitTableAvailable(tableName); 074 UTIL.loadTable(table, FAMILY); 075 } 076 } 077 078 @AfterAll 079 public static void afterClass() throws Exception { 080 for (TableName table : tables) { 081 UTIL.deleteTableIfAny(table); 082 } 083 UTIL.shutdownMiniCluster(); 084 } 085 086 @Test 087 public void testRegionMetrics() throws Exception { 088 089 // Check if regions match with the RegionMetrics from the server 090 for (ServerName serverName : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)) 091 .getLiveServerMetrics().keySet()) { 092 List<RegionInfo> regions = admin.getRegions(serverName); 093 Collection<RegionMetrics> regionMetricsList = admin.getRegionMetrics(serverName); 094 checkRegionsAndRegionMetrics(regions, regionMetricsList); 095 } 096 097 // Check if regionMetrics matches the table's regions and nothing is missed 098 for (TableName table : new TableName[] { TABLE_1, TABLE_2, TABLE_3 }) { 099 List<RegionInfo> tableRegions = admin.getRegions(table); 100 101 List<RegionMetrics> regionMetrics = new ArrayList<>(); 102 for (ServerName serverName : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)) 103 .getLiveServerMetrics().keySet()) { 104 regionMetrics.addAll(admin.getRegionMetrics(serverName, table)); 105 } 106 checkRegionsAndRegionMetrics(tableRegions, regionMetrics); 107 } 108 109 // Just wait here. If this fixes the test, come back and do a better job. 110 // Would have to redo the below so can wait on cluster status changing. 111 // Admin#getClusterMetrics retrieves data from HMaster. Admin#getRegionMetrics, by contrast, 112 // get the data from RS. Hence, it will fail if we do the assert check before RS has done 113 // the report. 114 TimeUnit.MILLISECONDS.sleep(3 * MSG_INTERVAL); 115 116 // Check RegionMetrics matches the RegionMetrics from ClusterMetrics 117 for (Map.Entry<ServerName, ServerMetrics> entry : admin 118 .getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)).getLiveServerMetrics().entrySet()) { 119 ServerName serverName = entry.getKey(); 120 ServerMetrics serverMetrics = entry.getValue(); 121 List<RegionMetrics> regionMetrics = admin.getRegionMetrics(serverName); 122 LOG.debug("serverName=" + serverName + ", getRegionLoads=" + serverMetrics.getRegionMetrics() 123 .keySet().stream().map(r -> Bytes.toString(r)).collect(Collectors.toList())); 124 LOG.debug("serverName=" + serverName + ", regionLoads=" + regionMetrics.stream() 125 .map(r -> Bytes.toString(r.getRegionName())).collect(Collectors.toList())); 126 assertEquals(serverMetrics.getRegionMetrics().size(), regionMetrics.size()); 127 checkMetricsValue(regionMetrics, serverMetrics); 128 } 129 } 130 131 private void checkMetricsValue(List<RegionMetrics> regionMetrics, ServerMetrics serverMetrics) 132 throws InvocationTargetException, IllegalAccessException { 133 for (RegionMetrics fromRM : regionMetrics) { 134 RegionMetrics fromSM = serverMetrics.getRegionMetrics().get(fromRM.getRegionName()); 135 Class clazz = RegionMetrics.class; 136 for (Method method : clazz.getMethods()) { 137 // check numeric values only 138 if ( 139 method.getReturnType().equals(Size.class) || method.getReturnType().equals(int.class) 140 || method.getReturnType().equals(long.class) 141 || method.getReturnType().equals(float.class) 142 ) { 143 Object valueRm = method.invoke(fromRM); 144 Object valueSM = method.invoke(fromSM); 145 assertEquals(valueRm.toString(), valueSM.toString(), 146 "Return values of method " + method.getName() + " are different"); 147 } 148 } 149 } 150 } 151 152 private void checkRegionsAndRegionMetrics(Collection<RegionInfo> regions, 153 Collection<RegionMetrics> regionMetrics) { 154 155 assertEquals(regions.size(), regionMetrics.size(), 156 "No of regions and regionMetrics doesn't match"); 157 158 Map<byte[], RegionMetrics> regionMetricsMap = Maps.newTreeMap(Bytes.BYTES_COMPARATOR); 159 for (RegionMetrics r : regionMetrics) { 160 regionMetricsMap.put(r.getRegionName(), r); 161 } 162 for (RegionInfo info : regions) { 163 assertTrue(regionMetricsMap.containsKey(info.getRegionName()), 164 "Region not in RegionMetricsMap region:" + info.getRegionNameAsString() + " regionMap: " 165 + regionMetricsMap); 166 } 167 } 168}