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}