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 org.apache.hadoop.conf.Configuration;
021import org.apache.yetus.audience.InterfaceAudience;
022
023/**
024 * Given the starting state of the regions and a potential ending state compute cost based upon the
025 * number of regions that have moved.
026 */
027@InterfaceAudience.Private
028class MoveCostFunction extends CostFunction {
029  private static final String MOVE_COST_KEY = "hbase.master.balancer.stochastic.moveCost";
030  private static final String MOVE_COST_OFFPEAK_KEY =
031    "hbase.master.balancer.stochastic.moveCost.offpeak";
032  private static final String MAX_MOVES_PERCENT_KEY =
033    "hbase.master.balancer.stochastic.maxMovePercent";
034  static final float DEFAULT_MOVE_COST = 7;
035  static final float DEFAULT_MOVE_COST_OFFPEAK = 3;
036  private static final int DEFAULT_MAX_MOVES = 600;
037  private static final float DEFAULT_MAX_MOVE_PERCENT = 1.0f;
038
039  private final float maxMovesPercent;
040  private final ClusterInfoProvider provider;
041  private final float moveCost;
042  private final float moveCostOffPeak;
043
044  MoveCostFunction(Configuration conf, ClusterInfoProvider provider) {
045    this.provider = provider;
046    // What percent of the number of regions a single run of the balancer can move.
047    maxMovesPercent = conf.getFloat(MAX_MOVES_PERCENT_KEY, DEFAULT_MAX_MOVE_PERCENT);
048    moveCost = conf.getFloat(MOVE_COST_KEY, DEFAULT_MOVE_COST);
049    moveCostOffPeak = conf.getFloat(MOVE_COST_OFFPEAK_KEY, DEFAULT_MOVE_COST_OFFPEAK);
050    // Initialize the multiplier so that addCostFunction will add this cost function.
051    // It may change during later evaluations, due to OffPeakHours.
052    this.setMultiplier(moveCost);
053  }
054
055  @Override
056  void prepare(BalancerClusterState cluster) {
057    super.prepare(cluster);
058    // Move cost multiplier should be the same cost or higher than the rest of the costs to ensure
059    // that large benefits are need to overcome the cost of a move.
060    if (provider.isOffPeakHour()) {
061      this.setMultiplier(moveCostOffPeak);
062    } else {
063      this.setMultiplier(moveCost);
064    }
065  }
066
067  @Override
068  protected double cost() {
069    // Try and size the max number of Moves, but always be prepared to move some.
070    int maxMoves = Math.max((int) (cluster.numRegions * maxMovesPercent), DEFAULT_MAX_MOVES);
071
072    double moveCost = cluster.numMovedRegions;
073
074    // Don't let this single balance move more than the max moves.
075    // This allows better scaling to accurately represent the actual cost of a move.
076    if (moveCost > maxMoves) {
077      return 1000000; // return a number much greater than any of the other cost
078    }
079
080    return scale(0, Math.min(cluster.numRegions, maxMoves), moveCost);
081  }
082}