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