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.ArrayList; 021import java.util.HashSet; 022import java.util.List; 023import java.util.Set; 024import org.apache.hadoop.hbase.ServerName; 025import org.apache.yetus.audience.InterfaceAudience; 026import org.slf4j.Logger; 027import org.slf4j.LoggerFactory; 028 029/** 030 * A simple candidate generator that attempts to move regions from the most-loaded servers to the 031 * least-loaded servers. 032 */ 033@InterfaceAudience.Private 034final class SlopFixingCandidateGenerator extends RegionPlanConditionalCandidateGenerator { 035 036 private static final Logger LOG = LoggerFactory.getLogger(SlopFixingCandidateGenerator.class); 037 038 private final float slop; 039 040 SlopFixingCandidateGenerator(BalancerConditionals balancerConditionals) { 041 super(balancerConditionals); 042 this.slop = balancerConditionals.getConf().getFloat(BaseLoadBalancer.REGIONS_SLOP_KEY, 043 BaseLoadBalancer.REGIONS_SLOP_DEFAULT); 044 } 045 046 @Override 047 BalanceAction generateCandidate(BalancerClusterState cluster, boolean isWeighing) { 048 boolean isTableIsolationEnabled = getBalancerConditionals().isTableIsolationEnabled(); 049 ClusterLoadState cs = new ClusterLoadState(cluster.clusterState); 050 float average = cs.getLoadAverage(); 051 int ceiling = (int) Math.ceil(average * (1 + slop)); 052 Set<Integer> sloppyServerIndices = new HashSet<>(); 053 for (int i = 0; i < cluster.numServers; i++) { 054 int regionCount = cluster.regionsPerServer[i].length; 055 if (regionCount > ceiling) { 056 sloppyServerIndices.add(i); 057 } 058 } 059 060 if (sloppyServerIndices.isEmpty()) { 061 LOG.trace("No action to take because no sloppy servers exist."); 062 return BalanceAction.NULL_ACTION; 063 } 064 065 List<MoveRegionAction> moves = new ArrayList<>(); 066 Set<ServerAndLoad> fixedServers = new HashSet<>(); 067 for (int sourceServer : sloppyServerIndices) { 068 if ( 069 isTableIsolationEnabled 070 && getBalancerConditionals().isServerHostingIsolatedTables(cluster, sourceServer) 071 ) { 072 // Don't fix sloppiness of servers hosting isolated tables 073 continue; 074 } 075 for (int regionIdx : cluster.regionsPerServer[sourceServer]) { 076 boolean regionFoundMove = false; 077 for (ServerAndLoad serverAndLoad : cs.getServersByLoad().keySet()) { 078 ServerName destinationServer = serverAndLoad.getServerName(); 079 int destinationServerIdx = cluster.serversToIndex.get(destinationServer.getAddress()); 080 int regionsOnDestination = cluster.regionsPerServer[destinationServerIdx].length; 081 if (regionsOnDestination < average) { 082 MoveRegionAction move = 083 new MoveRegionAction(regionIdx, sourceServer, destinationServerIdx); 084 if (willBeAccepted(cluster, move)) { 085 if (isWeighing) { 086 // Fast exit for weighing candidate 087 return move; 088 } 089 moves.add(move); 090 cluster.doAction(move); 091 regionFoundMove = true; 092 break; 093 } 094 } else { 095 fixedServers.add(serverAndLoad); 096 } 097 } 098 fixedServers.forEach(s -> cs.getServersByLoad().remove(s)); 099 fixedServers.clear(); 100 if (!regionFoundMove && LOG.isTraceEnabled()) { 101 LOG.trace("Could not find a destination for region {} from server {}.", regionIdx, 102 sourceServer); 103 } 104 if (cluster.regionsPerServer[sourceServer].length <= ceiling) { 105 break; 106 } 107 } 108 } 109 110 return batchMovesAndResetClusterState(cluster, moves); 111 } 112}