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 * Compute the cost of a potential cluster state from skew in number of primary regions on a
025 * cluster.
026 */
027@InterfaceAudience.Private
028class PrimaryRegionCountSkewCostFunction extends CostFunction {
029
030  private static final String PRIMARY_REGION_COUNT_SKEW_COST_KEY =
031    "hbase.master.balancer.stochastic.primaryRegionCountCost";
032  private static final float DEFAULT_PRIMARY_REGION_COUNT_SKEW_COST = 500;
033
034  private final DoubleArrayCost cost = new DoubleArrayCost();
035
036  PrimaryRegionCountSkewCostFunction(Configuration conf) {
037    // Load multiplier should be the greatest as primary regions serve majority of reads/writes.
038    this.setMultiplier(
039      conf.getFloat(PRIMARY_REGION_COUNT_SKEW_COST_KEY, DEFAULT_PRIMARY_REGION_COUNT_SKEW_COST));
040  }
041
042  private double computeCostForRegionServer(int regionServerIndex) {
043    int cost = 0;
044    for (int regionIdx : cluster.regionsPerServer[regionServerIndex]) {
045      if (regionIdx == cluster.regionIndexToPrimaryIndex[regionIdx]) {
046        cost++;
047      }
048    }
049    return cost;
050  }
051
052  @Override
053  void prepare(BalancerClusterState cluster) {
054    super.prepare(cluster);
055    if (!isNeeded()) {
056      return;
057    }
058    cost.prepare(cluster.numServers);
059    cost.applyCostsChange(costs -> {
060      for (int i = 0; i < costs.length; i++) {
061        costs[i] = computeCostForRegionServer(i);
062      }
063    });
064  }
065
066  @Override
067  protected void regionMoved(int region, int oldServer, int newServer) {
068    cost.applyCostsChange(costs -> {
069      if (region == cluster.regionIndexToPrimaryIndex[region]) {
070        if (oldServer >= 0) {
071          costs[oldServer]--;
072        }
073        costs[newServer]++;
074      }
075    });
076  }
077
078  @Override
079  boolean isNeeded() {
080    return cluster.hasRegionReplicas;
081  }
082
083  @Override
084  protected double cost() {
085    return cost.cost();
086  }
087}