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.Assert.assertEquals; 021import static org.junit.Assert.assertNotNull; 022import static org.junit.Assert.assertTrue; 023 024import java.io.IOException; 025import java.util.Collection; 026import java.util.EnumSet; 027import java.util.List; 028import java.util.Map; 029import java.util.TreeMap; 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.Table; 035import org.apache.hadoop.hbase.testclassification.MediumTests; 036import org.apache.hadoop.hbase.testclassification.MiscTests; 037import org.apache.hadoop.hbase.util.Bytes; 038import org.junit.AfterClass; 039import org.junit.BeforeClass; 040import org.junit.ClassRule; 041import org.junit.Test; 042import org.junit.experimental.categories.Category; 043import org.slf4j.Logger; 044import org.slf4j.LoggerFactory; 045 046import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 047import org.apache.hbase.thirdparty.com.google.common.collect.Maps; 048 049@Category({MiscTests.class, MediumTests.class}) 050public class TestRegionLoad { 051 052 @ClassRule 053 public static final HBaseClassTestRule CLASS_RULE = 054 HBaseClassTestRule.forClass(TestRegionLoad.class); 055 056 private static final Logger LOG = LoggerFactory.getLogger(TestRegionLoad.class); 057 private static final HBaseTestingUtility UTIL = new HBaseTestingUtility(); 058 private static Admin admin; 059 060 private static final TableName TABLE_1 = TableName.valueOf("table_1"); 061 private static final TableName TABLE_2 = TableName.valueOf("table_2"); 062 private static final TableName TABLE_3 = TableName.valueOf("table_3"); 063 private static final TableName[] tables = new TableName[]{TABLE_1, TABLE_2, TABLE_3}; 064 private static final int MSG_INTERVAL = 500; // ms 065 066 @BeforeClass 067 public static void beforeClass() throws Exception { 068 // Make servers report eagerly. This test is about looking at the cluster status reported. 069 // Make it so we don't have to wait around too long to see change. 070 UTIL.getConfiguration().setInt("hbase.regionserver.msginterval", MSG_INTERVAL); 071 UTIL.startMiniCluster(4); 072 admin = UTIL.getAdmin(); 073 admin.setBalancerRunning(false, true); 074 createTables(); 075 } 076 077 @AfterClass 078 public static void afterClass() throws Exception { 079 UTIL.shutdownMiniCluster(); 080 } 081 082 private static void createTables() throws IOException, InterruptedException { 083 byte[][] FAMILIES = new byte [][] {Bytes.toBytes("f")}; 084 for (TableName tableName : tables) { 085 Table table = 086 UTIL.createTable(tableName, FAMILIES, HBaseTestingUtility.KEYS_FOR_HBA_CREATE_TABLE); 087 UTIL.waitTableAvailable(tableName); 088 UTIL.loadTable(table, FAMILIES[0]); 089 } 090 } 091 092 @Test 093 public void testRegionLoad() throws Exception { 094 095 // Check if regions match with the regionLoad from the server 096 for (ServerName serverName : admin 097 .getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)).getLiveServerMetrics().keySet()) { 098 List<HRegionInfo> regions = admin.getOnlineRegions(serverName); 099 LOG.info("serverName=" + serverName + ", regions=" + 100 regions.stream().map(r -> r.getRegionNameAsString()).collect(Collectors.toList())); 101 Collection<RegionLoad> regionLoads = admin.getRegionMetrics(serverName) 102 .stream().map(r -> new RegionLoad(r)).collect(Collectors.toList()); 103 LOG.info("serverName=" + serverName + ", regionLoads=" + 104 regionLoads.stream().map(r -> Bytes.toString(r.getRegionName())). 105 collect(Collectors.toList())); 106 checkRegionsAndRegionLoads(regions, regionLoads); 107 } 108 109 // Check if regionLoad matches the table's regions and nothing is missed 110 for (TableName table : new TableName[]{TABLE_1, TABLE_2, TABLE_3}) { 111 List<HRegionInfo> tableRegions = admin.getTableRegions(table); 112 113 List<RegionLoad> regionLoads = Lists.newArrayList(); 114 for (ServerName serverName : admin 115 .getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)).getLiveServerMetrics().keySet()) { 116 regionLoads.addAll(admin.getRegionMetrics(serverName, table) 117 .stream().map(r -> new RegionLoad(r)).collect(Collectors.toList())); 118 } 119 checkRegionsAndRegionLoads(tableRegions, regionLoads); 120 } 121 122 // Just wait here. If this fixes the test, come back and do a better job. 123 // Would have to redo the below so can wait on cluster status changing. 124 // Admin#getClusterMetrics retrieves data from HMaster. Admin#getRegionMetrics, by contrast, 125 // get the data from RS. Hence, it will fail if we do the assert check before RS has done 126 // the report. 127 TimeUnit.MILLISECONDS.sleep(3 * MSG_INTERVAL); 128 129 // Check RegionLoad matches the regionLoad from ClusterStatus 130 ClusterStatus clusterStatus 131 = new ClusterStatus(admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))); 132 for (ServerName serverName : clusterStatus.getServers()) { 133 ServerLoad serverLoad = clusterStatus.getLoad(serverName); 134 Map<byte[], RegionLoad> regionLoads = admin.getRegionMetrics(serverName).stream() 135 .collect(Collectors.toMap(e -> e.getRegionName(), e -> new RegionLoad(e), 136 (v1, v2) -> { 137 throw new RuntimeException("impossible!!"); 138 }, () -> new TreeMap<>(Bytes.BYTES_COMPARATOR))); 139 LOG.debug("serverName=" + serverName + ", getRegionLoads=" + 140 serverLoad.getRegionsLoad().keySet().stream().map(r -> Bytes.toString(r)). 141 collect(Collectors.toList())); 142 LOG.debug("serverName=" + serverName + ", regionLoads=" + 143 regionLoads.keySet().stream().map(r -> Bytes.toString(r)). 144 collect(Collectors.toList())); 145 compareRegionLoads(serverLoad.getRegionsLoad(), regionLoads); 146 } 147 } 148 149 private void compareRegionLoads(Map<byte[], RegionLoad> regionLoadCluster, 150 Map<byte[], RegionLoad> regionLoads) { 151 152 assertEquals("No of regionLoads from clusterStatus and regionloads from RS doesn't match", 153 regionLoadCluster.size(), regionLoads.size()); 154 155 // The contents of region load from cluster and server should match 156 for (byte[] regionName : regionLoadCluster.keySet()) { 157 regionLoads.remove(regionName); 158 } 159 assertEquals("regionLoads from SN should be empty", 0, regionLoads.size()); 160 } 161 162 private void checkRegionsAndRegionLoads(Collection<HRegionInfo> regions, 163 Collection<RegionLoad> regionLoads) { 164 165 for (RegionLoad load : regionLoads) { 166 assertNotNull(load.regionLoadPB); 167 } 168 169 assertEquals("No of regions and regionloads doesn't match", 170 regions.size(), regionLoads.size()); 171 172 Map<byte[], RegionLoad> regionLoadMap = Maps.newTreeMap(Bytes.BYTES_COMPARATOR); 173 for (RegionLoad regionLoad : regionLoads) { 174 regionLoadMap.put(regionLoad.getName(), regionLoad); 175 } 176 for (HRegionInfo info : regions) { 177 assertTrue("Region not in regionLoadMap region:" + info.getRegionNameAsString() + 178 " regionMap: " + regionLoadMap, regionLoadMap.containsKey(info.getRegionName())); 179 } 180 } 181}