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 */ 018 019package org.apache.hadoop.hbase.master.balancer; 020 021import org.apache.hadoop.hbase.master.MasterServices; 022 023import org.apache.hbase.thirdparty.com.google.common.base.Optional; 024 025import org.apache.yetus.audience.InterfaceAudience; 026 027@InterfaceAudience.Private 028class LocalityBasedCandidateGenerator extends CandidateGenerator { 029 030 private MasterServices masterServices; 031 032 LocalityBasedCandidateGenerator(MasterServices masterServices) { 033 this.masterServices = masterServices; 034 } 035 036 @Override 037 BaseLoadBalancer.Cluster.Action generate(BaseLoadBalancer.Cluster cluster) { 038 if (this.masterServices == null) { 039 int thisServer = pickRandomServer(cluster); 040 // Pick the other server 041 int otherServer = pickOtherRandomServer(cluster, thisServer); 042 return pickRandomRegions(cluster, thisServer, otherServer); 043 } 044 045 // Randomly iterate through regions until you find one that is not on ideal host 046 for (int region : getRandomIterationOrder(cluster.numRegions)) { 047 int currentServer = cluster.regionIndexToServerIndex[region]; 048 if (currentServer != cluster.getOrComputeRegionsToMostLocalEntities( 049 BaseLoadBalancer.Cluster.LocalityType.SERVER)[region]) { 050 Optional<BaseLoadBalancer.Cluster.Action> potential = tryMoveOrSwap(cluster, 051 currentServer, region, 052 cluster.getOrComputeRegionsToMostLocalEntities( 053 BaseLoadBalancer.Cluster.LocalityType.SERVER)[region] 054 ); 055 if (potential.isPresent()) { 056 return potential.get(); 057 } 058 } 059 } 060 return BaseLoadBalancer.Cluster.NullAction; 061 } 062 063 private Optional<BaseLoadBalancer.Cluster.Action> tryMoveOrSwap(BaseLoadBalancer.Cluster cluster, 064 int fromServer, int fromRegion, int toServer) { 065 // Try move first. We know apriori fromRegion has the highest locality on toServer 066 if (cluster.serverHasTooFewRegions(toServer)) { 067 return Optional.of(getAction(fromServer, fromRegion, toServer, -1)); 068 } 069 // Compare locality gain/loss from swapping fromRegion with regions on toServer 070 double fromRegionLocalityDelta = getWeightedLocality(cluster, fromRegion, toServer) 071 - getWeightedLocality(cluster, fromRegion, fromServer); 072 for (int toRegionIndex : getRandomIterationOrder(cluster.regionsPerServer[toServer].length)) { 073 int toRegion = cluster.regionsPerServer[toServer][toRegionIndex]; 074 double toRegionLocalityDelta = getWeightedLocality(cluster, toRegion, fromServer) 075 - getWeightedLocality(cluster, toRegion, toServer); 076 // If locality would remain neutral or improve, attempt the swap 077 if (fromRegionLocalityDelta + toRegionLocalityDelta >= 0) { 078 return Optional.of(getAction(fromServer, fromRegion, toServer, toRegion)); 079 } 080 } 081 return Optional.absent(); 082 } 083 084 private double getWeightedLocality(BaseLoadBalancer.Cluster cluster, int region, int server) { 085 return cluster.getOrComputeWeightedLocality(region, server, 086 BaseLoadBalancer.Cluster.LocalityType.SERVER); 087 } 088 089 void setServices(MasterServices services) { 090 this.masterServices = services; 091 } 092 093}