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}