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.master.balancer.BalancerClusterState.LocalityType;
022import org.apache.yetus.audience.InterfaceAudience;
023
024/**
025 * Compute a cost of a potential cluster configuration based upon where
026 * {@link org.apache.hadoop.hbase.regionserver.HStoreFile}s are located.
027 */
028@InterfaceAudience.Private
029abstract class LocalityBasedCostFunction extends CostFunction {
030
031  private final LocalityType type;
032
033  private double bestLocality; // best case locality across cluster weighted by local data size
034  private double locality; // current locality across cluster weighted by local data size
035
036  LocalityBasedCostFunction(Configuration conf, LocalityType type, String localityCostKey,
037    float defaultLocalityCost) {
038    this.type = type;
039    this.setMultiplier(conf.getFloat(localityCostKey, defaultLocalityCost));
040    this.locality = 0.0;
041    this.bestLocality = 0.0;
042  }
043
044  /**
045   * Maps region to the current entity (server or rack) on which it is stored
046   */
047  abstract int regionIndexToEntityIndex(int region);
048
049  @Override
050  void prepare(BalancerClusterState cluster) {
051    super.prepare(cluster);
052    locality = 0.0;
053    bestLocality = 0.0;
054
055    for (int region = 0; region < cluster.numRegions; region++) {
056      locality += getWeightedLocality(region, regionIndexToEntityIndex(region));
057      bestLocality += getWeightedLocality(region, getMostLocalEntityForRegion(region));
058    }
059
060    // We normalize locality to be a score between 0 and 1.0 representing how good it
061    // is compared to how good it could be. If bestLocality is 0, assume locality is 100
062    // (and the cost is 0)
063    locality = bestLocality == 0 ? 1.0 : locality / bestLocality;
064  }
065
066  @Override
067  protected void regionMoved(int region, int oldServer, int newServer) {
068    int oldEntity =
069      type == LocalityType.SERVER ? oldServer : cluster.serverIndexToRackIndex[oldServer];
070    int newEntity =
071      type == LocalityType.SERVER ? newServer : cluster.serverIndexToRackIndex[newServer];
072    double localityDelta =
073      getWeightedLocality(region, newEntity) - getWeightedLocality(region, oldEntity);
074    double normalizedDelta = bestLocality == 0 ? 0.0 : localityDelta / bestLocality;
075    locality += normalizedDelta;
076  }
077
078  @Override
079  protected final double cost() {
080    return 1 - locality;
081  }
082
083  private int getMostLocalEntityForRegion(int region) {
084    return cluster.getOrComputeRegionsToMostLocalEntities(type)[region];
085  }
086
087  private double getWeightedLocality(int region, int entity) {
088    return cluster.getOrComputeWeightedLocality(region, entity, type);
089  }
090
091  @Override
092  public final void updateWeight(double[] weights) {
093    weights[StochasticLoadBalancer.GeneratorType.LOCALITY.ordinal()] += cost();
094  }
095}