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.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
097      .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).stream()
102        .map(r -> new RegionLoad(r)).collect(Collectors.toList());
103      LOG.info("serverName=" + serverName + ", regionLoads=" + regionLoads.stream()
104        .map(r -> Bytes.toString(r.getRegionName())).collect(Collectors.toList()));
105      checkRegionsAndRegionLoads(regions, regionLoads);
106    }
107
108    // Check if regionLoad matches the table's regions and nothing is missed
109    for (TableName table : new TableName[] { TABLE_1, TABLE_2, TABLE_3 }) {
110      List<HRegionInfo> tableRegions = admin.getTableRegions(table);
111
112      List<RegionLoad> regionLoads = Lists.newArrayList();
113      for (ServerName serverName : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
114        .getLiveServerMetrics().keySet()) {
115        regionLoads.addAll(admin.getRegionMetrics(serverName, table).stream()
116          .map(r -> new RegionLoad(r)).collect(Collectors.toList()));
117      }
118      checkRegionsAndRegionLoads(tableRegions, regionLoads);
119    }
120
121    // Just wait here. If this fixes the test, come back and do a better job.
122    // Would have to redo the below so can wait on cluster status changing.
123    // Admin#getClusterMetrics retrieves data from HMaster. Admin#getRegionMetrics, by contrast,
124    // get the data from RS. Hence, it will fail if we do the assert check before RS has done
125    // the report.
126    TimeUnit.MILLISECONDS.sleep(3 * MSG_INTERVAL);
127
128    // Check RegionLoad matches the regionLoad from ClusterStatus
129    ClusterStatus clusterStatus =
130      new ClusterStatus(admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)));
131    for (ServerName serverName : clusterStatus.getServers()) {
132      ServerLoad serverLoad = clusterStatus.getLoad(serverName);
133      Map<byte[], RegionLoad> regionLoads = admin.getRegionMetrics(serverName).stream()
134        .collect(Collectors.toMap(e -> e.getRegionName(), e -> new RegionLoad(e), (v1, v2) -> {
135          throw new RuntimeException("impossible!!");
136        }, () -> new TreeMap<>(Bytes.BYTES_COMPARATOR)));
137      LOG.debug("serverName=" + serverName + ", getRegionLoads=" + serverLoad.getRegionsLoad()
138        .keySet().stream().map(r -> Bytes.toString(r)).collect(Collectors.toList()));
139      LOG.debug("serverName=" + serverName + ", regionLoads="
140        + regionLoads.keySet().stream().map(r -> Bytes.toString(r)).collect(Collectors.toList()));
141      compareRegionLoads(serverLoad.getRegionsLoad(), regionLoads);
142    }
143  }
144
145  private void compareRegionLoads(Map<byte[], RegionLoad> regionLoadCluster,
146    Map<byte[], RegionLoad> regionLoads) {
147
148    assertEquals("No of regionLoads from clusterStatus and regionloads from RS doesn't match",
149      regionLoadCluster.size(), regionLoads.size());
150
151    // The contents of region load from cluster and server should match
152    for (byte[] regionName : regionLoadCluster.keySet()) {
153      regionLoads.remove(regionName);
154    }
155    assertEquals("regionLoads from SN should be empty", 0, regionLoads.size());
156  }
157
158  private void checkRegionsAndRegionLoads(Collection<HRegionInfo> regions,
159    Collection<RegionLoad> regionLoads) {
160
161    for (RegionLoad load : regionLoads) {
162      assertNotNull(load.regionLoadPB);
163    }
164
165    assertEquals("No of regions and regionloads doesn't match", regions.size(), regionLoads.size());
166
167    Map<byte[], RegionLoad> regionLoadMap = Maps.newTreeMap(Bytes.BYTES_COMPARATOR);
168    for (RegionLoad regionLoad : regionLoads) {
169      regionLoadMap.put(regionLoad.getName(), regionLoad);
170    }
171    for (HRegionInfo info : regions) {
172      assertTrue("Region not in regionLoadMap region:" + info.getRegionNameAsString()
173        + " regionMap: " + regionLoadMap, regionLoadMap.containsKey(info.getRegionName()));
174    }
175  }
176}