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