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.master.balancer;
019
020import java.io.IOException;
021import java.util.ArrayList;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026import java.util.concurrent.TimeUnit;
027import org.apache.hadoop.hbase.HBaseConfiguration;
028import org.apache.hadoop.hbase.HBaseInterfaceAudience;
029import org.apache.hadoop.hbase.HConstants;
030import org.apache.hadoop.hbase.ServerName;
031import org.apache.hadoop.hbase.TableName;
032import org.apache.hadoop.hbase.client.RegionInfo;
033import org.apache.hadoop.hbase.client.RegionInfoBuilder;
034import org.apache.hadoop.hbase.master.LoadBalancer;
035import org.apache.hadoop.hbase.util.AbstractHBaseTool;
036import org.apache.hadoop.hbase.util.Bytes;
037import org.apache.yetus.audience.InterfaceAudience;
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040
041import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
042import org.apache.hbase.thirdparty.com.google.common.base.Stopwatch;
043import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine;
044import org.apache.hbase.thirdparty.org.apache.commons.cli.Option;
045
046/**
047 * Tool to test performance of different {@link org.apache.hadoop.hbase.master.LoadBalancer}
048 * implementations. Example command: $ bin/hbase
049 * org.apache.hadoop.hbase.master.balancer.LoadBalancerPerformanceEvaluation -regions 1000 -servers
050 * 100 -load_balancer org.apache.hadoop.hbase.master.balancer.SimpleLoadBalancer
051 */
052@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS)
053public class LoadBalancerPerformanceEvaluation extends AbstractHBaseTool {
054  private static final Logger LOG =
055    LoggerFactory.getLogger(LoadBalancerPerformanceEvaluation.class.getName());
056
057  private static final int DEFAULT_NUM_REGIONS = 1000000;
058  private static Option NUM_REGIONS_OPT = new Option("regions", true,
059    "Number of regions to consider by load balancer. Default: " + DEFAULT_NUM_REGIONS);
060
061  private static final int DEFAULT_NUM_SERVERS = 1000;
062  private static Option NUM_SERVERS_OPT = new Option("servers", true,
063    "Number of servers to consider by load balancer. Default: " + DEFAULT_NUM_SERVERS);
064
065  private static final String DEFAULT_LOAD_BALANCER =
066    "org.apache.hadoop.hbase.master.balancer.StochasticLoadBalancer";
067  private static Option LOAD_BALANCER_OPT = new Option("load_balancer", true,
068    "Type of Load Balancer to use. Default: " + DEFAULT_LOAD_BALANCER);
069
070  private int numRegions;
071  private int numServers;
072  private String loadBalancerType;
073  private Class<?> loadBalancerClazz;
074
075  private LoadBalancer loadBalancer;
076
077  // data
078  private List<ServerName> servers;
079  private List<RegionInfo> regions;
080  private Map<RegionInfo, ServerName> regionServerMap;
081  private Map<TableName, Map<ServerName, List<RegionInfo>>> tableServerRegionMap;
082
083  // Non-default configurations.
084  private void setupConf() {
085    conf.setClass(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, loadBalancerClazz,
086      LoadBalancer.class);
087    loadBalancer = LoadBalancerFactory.getLoadBalancer(conf);
088  }
089
090  private void generateRegionsAndServers() {
091    TableName tableName = TableName.valueOf("LoadBalancerPerfTable");
092    // regions
093    regions = new ArrayList<>(numRegions);
094    regionServerMap = new HashMap<>(numRegions);
095    for (int i = 0; i < numRegions; ++i) {
096      byte[] start = new byte[16];
097      byte[] end = new byte[16];
098
099      Bytes.putInt(start, 0, i);
100      Bytes.putInt(end, 0, i + 1);
101      RegionInfo hri = RegionInfoBuilder.newBuilder(tableName).setStartKey(start).setEndKey(end)
102        .setSplit(false).setRegionId(i).build();
103      regions.add(hri);
104      regionServerMap.put(hri, null);
105    }
106
107    // servers
108    servers = new ArrayList<>(numServers);
109    Map<ServerName, List<RegionInfo>> serverRegionMap = new HashMap<>(numServers);
110    for (int i = 0; i < numServers; ++i) {
111      ServerName sn = ServerName.valueOf("srv" + i, HConstants.DEFAULT_REGIONSERVER_PORT, i);
112      servers.add(sn);
113      serverRegionMap.put(sn, i == 0 ? regions : Collections.emptyList());
114    }
115    tableServerRegionMap = Collections.singletonMap(tableName, serverRegionMap);
116  }
117
118  @Override
119  protected void addOptions() {
120    addOption(NUM_REGIONS_OPT);
121    addOption(NUM_SERVERS_OPT);
122    addOption(LOAD_BALANCER_OPT);
123  }
124
125  @Override
126  protected void processOptions(CommandLine cmd) {
127    numRegions = getOptionAsInt(cmd, NUM_REGIONS_OPT.getOpt(), DEFAULT_NUM_REGIONS);
128    Preconditions.checkArgument(numRegions > 0, "Invalid number of regions!");
129
130    numServers = getOptionAsInt(cmd, NUM_SERVERS_OPT.getOpt(), DEFAULT_NUM_SERVERS);
131    Preconditions.checkArgument(numServers > 0, "Invalid number of servers!");
132
133    loadBalancerType = cmd.getOptionValue(LOAD_BALANCER_OPT.getOpt(), DEFAULT_LOAD_BALANCER);
134    Preconditions.checkArgument(!loadBalancerType.isEmpty(), "Invalid load balancer type!");
135
136    try {
137      loadBalancerClazz = Class.forName(loadBalancerType);
138    } catch (ClassNotFoundException e) {
139      System.err.println("Class '" + loadBalancerType + "' not found!");
140      System.exit(1);
141    }
142
143    setupConf();
144  }
145
146  private String formatResults(final String methodName, final long timeMillis) {
147    return String.format("Time for %-25s: %dms%n", methodName, timeMillis);
148  }
149
150  @Override
151  protected int doWork() throws Exception {
152    generateRegionsAndServers();
153
154    String methodName = "roundRobinAssignment";
155    LOG.info("Calling " + methodName);
156    Stopwatch watch = Stopwatch.createStarted();
157    loadBalancer.roundRobinAssignment(regions, servers);
158    System.out.print(formatResults(methodName, watch.elapsed(TimeUnit.MILLISECONDS)));
159
160    methodName = "retainAssignment";
161    LOG.info("Calling " + methodName);
162    watch.reset().start();
163    loadBalancer.retainAssignment(regionServerMap, servers);
164    System.out.print(formatResults(methodName, watch.elapsed(TimeUnit.MILLISECONDS)));
165
166    methodName = "balanceCluster";
167    LOG.info("Calling " + methodName);
168    watch.reset().start();
169
170    loadBalancer.balanceCluster(tableServerRegionMap);
171    System.out.print(formatResults(methodName, watch.elapsed(TimeUnit.MILLISECONDS)));
172
173    return EXIT_SUCCESS;
174  }
175
176  public static void main(String[] args) throws IOException {
177    LoadBalancerPerformanceEvaluation tool = new LoadBalancerPerformanceEvaluation();
178    tool.setConf(HBaseConfiguration.create());
179    tool.run(args);
180  }
181}