com.kakao.hbase.manager.command.Balance.java Source code

Java tutorial

Introduction

Here is the source code for com.kakao.hbase.manager.command.Balance.java

Source

/*
 * Copyright 2015 Kakao Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.kakao.hbase.manager.command;

import com.kakao.hbase.ManagerArgs;
import com.kakao.hbase.common.Args;
import com.kakao.hbase.common.Constant;
import com.kakao.hbase.common.util.Util;
import com.kakao.hbase.specific.CommandAdapter;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.master.RegionPlan;
import org.apache.hadoop.hbase.util.Bytes;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

@SuppressWarnings("unused")
public class Balance implements Command {
    private static Map<HRegionInfo, ServerName> regionLocations = null;
    private final HBaseAdmin admin;
    private final Args args;
    private final String ruleParam;
    private final Set<String> tableNameSet;

    public Balance(HBaseAdmin admin, Args args) throws IOException {
        if (args.getOptionSet().nonOptionArguments().size() != 3) {
            throw new RuntimeException(Args.INVALID_ARGUMENTS);
        }

        this.admin = admin;
        this.args = args;
        ruleParam = (String) args.getOptionSet().nonOptionArguments().get(2);

        tableNameSet = Util.parseTableSet(admin, args);
        reset();
    }

    static void reset() {
        regionLocations = null;
    }

    public static String usage() {
        return "Balance regions evenly by one of the rules below. Move regions one by one except for default.\n"
                + "usage: " + Balance.class.getSimpleName().toLowerCase() + " <zookeeper quorum>"
                + " <table name(regex)> <rule> [options]\n" + "  rule:\n"
                + "    default  - hbase default balancer. asynchronous\n" + "    rr       - round robin\n"
                + "    rd       - random\n" + "    st       - stochastic load balancer\n" + "  options:\n"
                + "    --" + ManagerArgs.OPTION_TURN_BALANCER_OFF + ": During balancing turn balancer off.\n"
                + "    --" + ManagerArgs.OPTION_BALANCE_FACTOR + "=<factor>:"
                + " Stochastic load balancer will balance by this single highly weighted factor.\n" + "    --"
                + Args.OPTION_MOVE_ASYNC + ": Move regions asynchronously.\n" + "  factors:\n"
                + BalanceFactor.usage(4) + Args.commonUsage();
    }

    // does not contain catalog tables
    public static Map<HRegionInfo, ServerName> createRegionAssignmentMap(HBaseAdmin admin, Set<String> tableNameSet)
            throws IOException {
        if (regionLocations == null) {
            regionLocations = new HashMap<>();
            for (String tableName : tableNameSet) {
                try (HTable table = new HTable(admin.getConfiguration(), tableName)) {
                    regionLocations.putAll(table.getRegionLocations());
                }
            }
        }
        return regionLocations;
    }

    public static List<RegionPlan> makePlan(HBaseAdmin admin, Set<String> tableNameSet, BalanceFactor balanceFactor)
            throws IOException {
        Map<ServerName, List<HRegionInfo>> clusterState = CommandAdapter.initializeRegionMap(admin);

        for (Map.Entry<HRegionInfo, ServerName> entry : createRegionAssignmentMap(admin, tableNameSet).entrySet())
            clusterState.get(entry.getValue()).add(entry.getKey());

        Configuration conf = admin.getConfiguration();
        conf.setFloat("hbase.regions.slop", 0f);
        balanceFactor.setConf(conf);

        return CommandAdapter.makePlan(admin, clusterState, conf);
    }

    @Override
    public void run() throws Exception {
        boolean balancerRunning = false;

        try {
            balancerRunning = turnBalancerOff();

            BalanceRule rule = BalanceRule.valueOf(ruleParam.toUpperCase());
            if (rule.equals(BalanceRule.DEFAULT)) {
                if (!args.isForceProceed()) {
                    if (!Util.askProceed()) {
                        return;
                    }
                }
                admin.balancer();
                System.out.println("Run hbase default balancer. This is an asynchronous operation.");
            } else {
                List<RegionPlan> regionPlanList = rule.makePlan(admin, tableNameSet, args);
                BalanceFactor.printFactor(BalanceFactor.parseArg(args));

                boolean asynchronous = args.has(Args.OPTION_MOVE_ASYNC);
                if (preview(regionPlanList, asynchronous))
                    balance(args, regionPlanList, Phase.BALANCE, asynchronous);
            }
        } finally {
            if (balancerRunning) {
                // turn balancer on if needed
                admin.setBalancerRunning(true, true);
                System.out.println("Turn balancer on.");
            }
        }
    }

    @SuppressWarnings("SimplifiableIfStatement")
    private boolean preview(List<RegionPlan> regionPlanList, boolean asynchronous)
            throws IOException, InterruptedException {
        final boolean proceed;
        if (args.isForceProceed()) {
            proceed = true;
        } else {
            balance(args, regionPlanList, Phase.PREVIEW, asynchronous);
            if (regionPlanList.size() > 0) {
                System.out.println(regionPlanList.size() + " of "
                        + createRegionAssignmentMap(admin, tableNameSet).size() + " region(s) will be moved.");
                warnBalanceAgain(regionPlanList);
                proceed = Util.askProceed();
            } else {
                System.out.println("There is no region to move.");
                proceed = false;
            }
        }

        return proceed;
    }

    private boolean warnBalanceAgain(List<RegionPlan> regionPlanList) throws IOException {
        List<RegionPlan> allTablePlanList = CommandAdapter.makePlan(admin, regionPlanList);
        if (allTablePlanList != null && allTablePlanList.size() > 0) {
            System.out.println("Warning - Default load balancer will balance the cluster again. "
                    + allTablePlanList.size() + " regions may be re-balanced.");
            return true;
        } else
            return false;
    }

    private boolean turnBalancerOff() throws IOException {
        if (args.getOptionSet().has(ManagerArgs.OPTION_TURN_BALANCER_OFF)) {
            boolean balancerRunning;
            balancerRunning = admin.setBalancerRunning(false, true);
            if (balancerRunning)
                System.out.println("Turn balancer off");
            return balancerRunning;
        } else {
            return false;
        }
    }

    @SuppressWarnings("deprecation")
    private void balance(Args args, List<RegionPlan> regionPlanList, Phase phase, boolean asynchronous)
            throws IOException, InterruptedException {
        int progress = 1;
        for (RegionPlan regionPlan : regionPlanList) {
            String tableName = Bytes.toString(regionPlan.getRegionInfo().getTableName());
            String encodedRegionName = regionPlan.getRegionInfo().getEncodedName();
            String serverNameDest = regionPlan.getDestination().getServerName();
            String serverNameSource;
            if (regionPlan.getSource() == null) {
                serverNameSource = "N/A";
            } else {
                serverNameSource = regionPlan.getSource().getServerName();
            }
            String planStr = progress++ + "/" + regionPlanList.size() + " - move " + encodedRegionName + " of "
                    + tableName + " from " + serverNameSource + " to " + serverNameDest;
            if (phase == Phase.BALANCE) {
                System.out.print(planStr);
            } else {
                System.out.println(planStr);
            }

            if (phase == Phase.BALANCE) {
                Common.moveWithPrintingResult(args, admin, tableName, encodedRegionName, serverNameDest,
                        asynchronous);
            }
        }

        if (asynchronous && phase == Phase.BALANCE)
            Thread.sleep(Constant.SMALL_WAIT_INTERVAL_MS);
    }

    enum Phase {
        BALANCE, PREVIEW
    }
}