/** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hbase.master; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.favored.FavoredNodeAssignmentHelper; import org.apache.hadoop.hbase.favored.FavoredNodesPlan; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.TableName; /** * Helper class that is used by {@link RegionPlacementMaintainer} to print * information for favored nodes * */ @InterfaceAudience.Private public class AssignmentVerificationReport { private static final Log LOG = LogFactory.getLog( AssignmentVerificationReport.class.getName()); private TableName tableName = null; private boolean enforceLocality = false; private boolean isFilledUp = false; private int totalRegions = 0; private int totalRegionServers = 0; // for unassigned regions private List<HRegionInfo> unAssignedRegionsList = new ArrayList<>(); // For regions without valid favored nodes private List<HRegionInfo> regionsWithoutValidFavoredNodes = new ArrayList<>(); // For regions not running on the favored nodes private List<HRegionInfo> nonFavoredAssignedRegionList = new ArrayList<>(); // For regions running on the favored nodes private int totalFavoredAssignments = 0; private int[] favoredNodes = new int[FavoredNodeAssignmentHelper.FAVORED_NODES_NUM]; private float[] favoredNodesLocalitySummary = new float[FavoredNodeAssignmentHelper.FAVORED_NODES_NUM]; private float actualLocalitySummary = 0; // For region balancing information private float avgRegionsOnRS = 0; private int maxRegionsOnRS = 0; private int minRegionsOnRS = Integer.MAX_VALUE; private Set<ServerName> mostLoadedRSSet = new HashSet<>(); private Set<ServerName> leastLoadedRSSet = new HashSet<>(); private float avgDispersionScore = 0; private float maxDispersionScore = 0; private Set<ServerName> maxDispersionScoreServerSet = new HashSet<>(); private float minDispersionScore = Float.MAX_VALUE; private Set<ServerName> minDispersionScoreServerSet = new HashSet<>(); private float avgDispersionNum = 0; private float maxDispersionNum = 0; private Set<ServerName> maxDispersionNumServerSet = new HashSet<>(); private float minDispersionNum = Float.MAX_VALUE; private Set<ServerName> minDispersionNumServerSet = new HashSet<>(); public void fillUp(TableName tableName, SnapshotOfRegionAssignmentFromMeta snapshot, Map<String, Map<String, Float>> regionLocalityMap) { // Set the table name this.tableName = tableName; // Get all the regions for this table List<HRegionInfo> regionInfoList = snapshot.getTableToRegionMap().get(tableName); // Get the total region num for the current table this.totalRegions = regionInfoList.size(); // Get the existing assignment plan FavoredNodesPlan favoredNodesAssignment = snapshot.getExistingAssignmentPlan(); // Get the region to region server mapping Map<HRegionInfo, ServerName> currentAssignment = snapshot.getRegionToRegionServerMap(); // Initialize the server to its hosing region counter map Map<ServerName, Integer> serverToHostingRegionCounterMap = new HashMap<>(); Map<ServerName, Integer> primaryRSToRegionCounterMap = new HashMap<>(); Map<ServerName, Set<ServerName>> primaryToSecTerRSMap = new HashMap<>(); // Check the favored nodes and its locality information // Also keep tracker of the most loaded and least loaded region servers for (HRegionInfo region : regionInfoList) { try { ServerName currentRS = currentAssignment.get(region); // Handle unassigned regions if (currentRS == null) { unAssignedRegionsList.add(region); continue; } // Keep updating the server to is hosting region counter map Integer hostRegionCounter = serverToHostingRegionCounterMap.get(currentRS); if (hostRegionCounter == null) { hostRegionCounter = Integer.valueOf(0); } hostRegionCounter = hostRegionCounter.intValue() + 1; serverToHostingRegionCounterMap.put(currentRS, hostRegionCounter); // Get the favored nodes from the assignment plan and verify it. List<ServerName> favoredNodes = favoredNodesAssignment.getFavoredNodes(region); if (favoredNodes == null || favoredNodes.size() != FavoredNodeAssignmentHelper.FAVORED_NODES_NUM) { regionsWithoutValidFavoredNodes.add(region); continue; } // Get the primary, secondary and tertiary region server ServerName primaryRS = favoredNodes.get(FavoredNodesPlan.Position.PRIMARY.ordinal()); ServerName secondaryRS = favoredNodes.get(FavoredNodesPlan.Position.SECONDARY.ordinal()); ServerName tertiaryRS = favoredNodes.get(FavoredNodesPlan.Position.TERTIARY.ordinal()); // Update the primary rs to its region set map Integer regionCounter = primaryRSToRegionCounterMap.get(primaryRS); if (regionCounter == null) { regionCounter = Integer.valueOf(0); } regionCounter = regionCounter.intValue() + 1; primaryRSToRegionCounterMap.put(primaryRS, regionCounter); // Update the primary rs to secondary and tertiary rs map Set<ServerName> secAndTerSet = primaryToSecTerRSMap.get(primaryRS); if (secAndTerSet == null) { secAndTerSet = new HashSet<>(); } secAndTerSet.add(secondaryRS); secAndTerSet.add(tertiaryRS); primaryToSecTerRSMap.put(primaryRS, secAndTerSet); // Get the position of the current region server in the favored nodes list FavoredNodesPlan.Position favoredNodePosition = FavoredNodesPlan.getFavoredServerPosition(favoredNodes, currentRS); // Handle the non favored assignment. if (favoredNodePosition == null) { nonFavoredAssignedRegionList.add(region); continue; } // Increase the favored nodes assignment. this.favoredNodes[favoredNodePosition.ordinal()]++; totalFavoredAssignments++; // Summary the locality information for each favored nodes if (regionLocalityMap != null) { // Set the enforce locality as true; this.enforceLocality = true; // Get the region degree locality map Map<String, Float> regionDegreeLocalityMap = regionLocalityMap.get(region.getEncodedName()); if (regionDegreeLocalityMap == null) { continue; // ignore the region which doesn't have any store files. } // Get the locality summary for each favored nodes for (FavoredNodesPlan.Position p : FavoredNodesPlan.Position.values()) { ServerName favoredNode = favoredNodes.get(p.ordinal()); // Get the locality for the current favored nodes Float locality = regionDegreeLocalityMap.get(favoredNode.getHostname()); if (locality != null) { this.favoredNodesLocalitySummary[p.ordinal()] += locality; } } // Get the locality summary for the current region server Float actualLocality = regionDegreeLocalityMap.get(currentRS.getHostname()); if (actualLocality != null) { this.actualLocalitySummary += actualLocality; } } } catch (Exception e) { LOG.error("Cannot verify the region assignment for region " + ((region == null) ? " null " : region.getRegionNameAsString()) + "because of " + e); } } float dispersionScoreSummary = 0; float dispersionNumSummary = 0; // Calculate the secondary score for each primary region server for (Map.Entry<ServerName, Integer> entry : primaryRSToRegionCounterMap.entrySet()) { ServerName primaryRS = entry.getKey(); Integer regionsOnPrimary = entry.getValue(); // Process the dispersion number and score float dispersionScore = 0; int dispersionNum = 0; if (primaryToSecTerRSMap.get(primaryRS) != null && regionsOnPrimary.intValue() != 0) { dispersionNum = primaryToSecTerRSMap.get(primaryRS).size(); dispersionScore = dispersionNum / ((float) regionsOnPrimary.intValue() * 2); } // Update the max dispersion score if (dispersionScore > this.maxDispersionScore) { this.maxDispersionScoreServerSet.clear(); this.maxDispersionScoreServerSet.add(primaryRS); this.maxDispersionScore = dispersionScore; } else if (dispersionScore == this.maxDispersionScore) { this.maxDispersionScoreServerSet.add(primaryRS); } // Update the max dispersion num if (dispersionNum > this.maxDispersionNum) { this.maxDispersionNumServerSet.clear(); this.maxDispersionNumServerSet.add(primaryRS); this.maxDispersionNum = dispersionNum; } else if (dispersionNum == this.maxDispersionNum) { this.maxDispersionNumServerSet.add(primaryRS); } // Update the min dispersion score if (dispersionScore < this.minDispersionScore) { this.minDispersionScoreServerSet.clear(); this.minDispersionScoreServerSet.add(primaryRS); this.minDispersionScore = dispersionScore; } else if (dispersionScore == this.minDispersionScore) { this.minDispersionScoreServerSet.add(primaryRS); } // Update the min dispersion num if (dispersionNum < this.minDispersionNum) { this.minDispersionNumServerSet.clear(); this.minDispersionNumServerSet.add(primaryRS); this.minDispersionNum = dispersionNum; } else if (dispersionNum == this.minDispersionNum) { this.minDispersionNumServerSet.add(primaryRS); } dispersionScoreSummary += dispersionScore; dispersionNumSummary += dispersionNum; } // Update the avg dispersion score if (primaryRSToRegionCounterMap.keySet().size() != 0) { this.avgDispersionScore = dispersionScoreSummary / (float) primaryRSToRegionCounterMap.keySet().size(); this.avgDispersionNum = dispersionNumSummary / (float) primaryRSToRegionCounterMap.keySet().size(); } // Fill up the most loaded and least loaded region server information for (Map.Entry<ServerName, Integer> entry : serverToHostingRegionCounterMap.entrySet()) { ServerName currentRS = entry.getKey(); int hostRegionCounter = entry.getValue().intValue(); // Update the most loaded region server list and maxRegionsOnRS if (hostRegionCounter > this.maxRegionsOnRS) { maxRegionsOnRS = hostRegionCounter; this.mostLoadedRSSet.clear(); this.mostLoadedRSSet.add(currentRS); } else if (hostRegionCounter == this.maxRegionsOnRS) { this.mostLoadedRSSet.add(currentRS); } // Update the least loaded region server list and minRegionsOnRS if (hostRegionCounter < this.minRegionsOnRS) { this.minRegionsOnRS = hostRegionCounter; this.leastLoadedRSSet.clear(); this.leastLoadedRSSet.add(currentRS); } else if (hostRegionCounter == this.minRegionsOnRS) { this.leastLoadedRSSet.add(currentRS); } } // and total region servers this.totalRegionServers = serverToHostingRegionCounterMap.keySet().size(); this.avgRegionsOnRS = (totalRegionServers == 0) ? 0 : (totalRegions / (float) totalRegionServers); // Set the isFilledUp as true isFilledUp = true; } /** * Use this to project the dispersion scores * @param tableName * @param snapshot * @param newPlan */ public void fillUpDispersion(TableName tableName, SnapshotOfRegionAssignmentFromMeta snapshot, FavoredNodesPlan newPlan) { // Set the table name this.tableName = tableName; // Get all the regions for this table List<HRegionInfo> regionInfoList = snapshot.getTableToRegionMap().get( tableName); // Get the total region num for the current table this.totalRegions = regionInfoList.size(); FavoredNodesPlan plan = null; if (newPlan == null) { plan = snapshot.getExistingAssignmentPlan(); } else { plan = newPlan; } // Get the region to region server mapping Map<ServerName, Integer> primaryRSToRegionCounterMap = new HashMap<>(); Map<ServerName, Set<ServerName>> primaryToSecTerRSMap = new HashMap<>(); // Check the favored nodes and its locality information // Also keep tracker of the most loaded and least loaded region servers for (HRegionInfo region : regionInfoList) { try { // Get the favored nodes from the assignment plan and verify it. List<ServerName> favoredNodes = plan.getFavoredNodes(region); if (favoredNodes == null || favoredNodes.size() != FavoredNodeAssignmentHelper.FAVORED_NODES_NUM) { regionsWithoutValidFavoredNodes.add(region); continue; } // Get the primary, secondary and tertiary region server ServerName primaryRS = favoredNodes .get(FavoredNodesPlan.Position.PRIMARY.ordinal()); ServerName secondaryRS = favoredNodes .get(FavoredNodesPlan.Position.SECONDARY.ordinal()); ServerName tertiaryRS = favoredNodes .get(FavoredNodesPlan.Position.TERTIARY.ordinal()); // Update the primary rs to its region set map Integer regionCounter = primaryRSToRegionCounterMap.get(primaryRS); if (regionCounter == null) { regionCounter = Integer.valueOf(0); } regionCounter = regionCounter.intValue() + 1; primaryRSToRegionCounterMap.put(primaryRS, regionCounter); // Update the primary rs to secondary and tertiary rs map Set<ServerName> secAndTerSet = primaryToSecTerRSMap.get(primaryRS); if (secAndTerSet == null) { secAndTerSet = new HashSet<>(); } secAndTerSet.add(secondaryRS); secAndTerSet.add(tertiaryRS); primaryToSecTerRSMap.put(primaryRS, secAndTerSet); } catch (Exception e) { LOG.error("Cannot verify the region assignment for region " + ((region == null) ? " null " : region.getRegionNameAsString()) + "because of " + e); } } float dispersionScoreSummary = 0; float dispersionNumSummary = 0; // Calculate the secondary score for each primary region server for (Map.Entry<ServerName, Integer> entry : primaryRSToRegionCounterMap.entrySet()) { ServerName primaryRS = entry.getKey(); Integer regionsOnPrimary = entry.getValue(); // Process the dispersion number and score float dispersionScore = 0; int dispersionNum = 0; if (primaryToSecTerRSMap.get(primaryRS) != null && regionsOnPrimary.intValue() != 0) { dispersionNum = primaryToSecTerRSMap.get(primaryRS).size(); dispersionScore = dispersionNum / ((float) regionsOnPrimary.intValue() * 2); } // Update the max dispersion num if (dispersionNum > this.maxDispersionNum) { this.maxDispersionNumServerSet.clear(); this.maxDispersionNumServerSet.add(primaryRS); this.maxDispersionNum = dispersionNum; } else if (dispersionNum == this.maxDispersionNum) { this.maxDispersionNumServerSet.add(primaryRS); } // Update the min dispersion score if (dispersionScore < this.minDispersionScore) { this.minDispersionScoreServerSet.clear(); this.minDispersionScoreServerSet.add(primaryRS); this.minDispersionScore = dispersionScore; } else if (dispersionScore == this.minDispersionScore) { this.minDispersionScoreServerSet.add(primaryRS); } // Update the min dispersion num if (dispersionNum < this.minDispersionNum) { this.minDispersionNumServerSet.clear(); this.minDispersionNumServerSet.add(primaryRS); this.minDispersionNum = dispersionNum; } else if (dispersionNum == this.minDispersionNum) { this.minDispersionNumServerSet.add(primaryRS); } dispersionScoreSummary += dispersionScore; dispersionNumSummary += dispersionNum; } // Update the avg dispersion score if (primaryRSToRegionCounterMap.keySet().size() != 0) { this.avgDispersionScore = dispersionScoreSummary / (float) primaryRSToRegionCounterMap.keySet().size(); this.avgDispersionNum = dispersionNumSummary / (float) primaryRSToRegionCounterMap.keySet().size(); } } /** * @return list which contains just 3 elements: average dispersion score, max * dispersion score and min dispersion score as first, second and third element * respectively. * */ public List<Float> getDispersionInformation() { List<Float> dispersion = new ArrayList<>(); dispersion.add(avgDispersionScore); dispersion.add(maxDispersionScore); dispersion.add(minDispersionScore); return dispersion; } public void print(boolean isDetailMode) { if (!isFilledUp) { System.err.println("[Error] Region assignment verification report" + "hasn't been filled up"); } DecimalFormat df = new java.text.DecimalFormat( "#.##"); // Print some basic information System.out.println("Region Assignment Verification for Table: " + tableName + "\n\tTotal regions : " + totalRegions); // Print the number of regions on each kinds of the favored nodes System.out.println("\tTotal regions on favored nodes " + totalFavoredAssignments); for (FavoredNodesPlan.Position p : FavoredNodesPlan.Position.values()) { System.out.println("\t\tTotal regions on "+ p.toString() + " region servers: " + favoredNodes[p.ordinal()]); } // Print the number of regions in each kinds of invalid assignment System.out.println("\tTotal unassigned regions: " + unAssignedRegionsList.size()); if (isDetailMode) { for (HRegionInfo region : unAssignedRegionsList) { System.out.println("\t\t" + region.getRegionNameAsString()); } } System.out.println("\tTotal regions NOT on favored nodes: " + nonFavoredAssignedRegionList.size()); if (isDetailMode) { for (HRegionInfo region : nonFavoredAssignedRegionList) { System.out.println("\t\t" + region.getRegionNameAsString()); } } System.out.println("\tTotal regions without favored nodes: " + regionsWithoutValidFavoredNodes.size()); if (isDetailMode) { for (HRegionInfo region : regionsWithoutValidFavoredNodes) { System.out.println("\t\t" + region.getRegionNameAsString()); } } // Print the locality information if enabled if (this.enforceLocality && totalRegions != 0) { // Print the actual locality for this table float actualLocality = 100 * this.actualLocalitySummary / (float) totalRegions; System.out.println("\n\tThe actual avg locality is " + df.format(actualLocality) + " %"); // Print the expected locality if regions are placed on the each kinds of // favored nodes for (FavoredNodesPlan.Position p : FavoredNodesPlan.Position.values()) { float avgLocality = 100 * (favoredNodesLocalitySummary[p.ordinal()] / (float) totalRegions); System.out.println("\t\tThe expected avg locality if all regions" + " on the " + p.toString() + " region servers: " + df.format(avgLocality) + " %"); } } // Print the region balancing information System.out.println("\n\tTotal hosting region servers: " + totalRegionServers); // Print the region balance information if (totalRegionServers != 0) { System.out.println( "\tAvg dispersion num: " +df.format(avgDispersionNum) + " hosts;\tMax dispersion num: " + df.format(maxDispersionNum) + " hosts;\tMin dispersion num: " + df.format(minDispersionNum) + " hosts;"); System.out.println("\t\tThe number of the region servers with the max" + " dispersion num: " + this.maxDispersionNumServerSet.size()); if (isDetailMode) { printHServerAddressSet(maxDispersionNumServerSet); } System.out.println("\t\tThe number of the region servers with the min" + " dispersion num: " + this.minDispersionNumServerSet.size()); if (isDetailMode) { printHServerAddressSet(maxDispersionNumServerSet); } System.out.println( "\tAvg dispersion score: " + df.format(avgDispersionScore) + ";\tMax dispersion score: " + df.format(maxDispersionScore) + ";\tMin dispersion score: " + df.format(minDispersionScore) + ";"); System.out.println("\t\tThe number of the region servers with the max" + " dispersion score: " + this.maxDispersionScoreServerSet.size()); if (isDetailMode) { printHServerAddressSet(maxDispersionScoreServerSet); } System.out.println("\t\tThe number of the region servers with the min" + " dispersion score: " + this.minDispersionScoreServerSet.size()); if (isDetailMode) { printHServerAddressSet(minDispersionScoreServerSet); } System.out.println( "\tAvg regions/region server: " + df.format(avgRegionsOnRS) + ";\tMax regions/region server: " + maxRegionsOnRS + ";\tMin regions/region server: " + minRegionsOnRS + ";"); // Print the details about the most loaded region servers System.out.println("\t\tThe number of the most loaded region servers: " + mostLoadedRSSet.size()); if (isDetailMode) { printHServerAddressSet(mostLoadedRSSet); } // Print the details about the least loaded region servers System.out.println("\t\tThe number of the least loaded region servers: " + leastLoadedRSSet.size()); if (isDetailMode) { printHServerAddressSet(leastLoadedRSSet); } } System.out.println("=============================="); } /** * Return the unassigned regions * @return unassigned regions */ List<HRegionInfo> getUnassignedRegions() { return unAssignedRegionsList; } /** * Return the regions without favored nodes * @return regions without favored nodes */ List<HRegionInfo> getRegionsWithoutValidFavoredNodes() { return regionsWithoutValidFavoredNodes; } /** * Return the regions not assigned to its favored nodes * @return regions not assigned to its favored nodes */ List<HRegionInfo> getNonFavoredAssignedRegions() { return nonFavoredAssignedRegionList; } /** * Return the number of regions assigned to their favored nodes * @return number of regions assigned to their favored nodes */ int getTotalFavoredAssignments() { return totalFavoredAssignments; } /** * Return the number of regions based on the position (primary/secondary/ * tertiary) assigned to their favored nodes * @param position * @return the number of regions */ int getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position position) { return favoredNodes[position.ordinal()]; } private void printHServerAddressSet(Set<ServerName> serverSet) { if (serverSet == null) { return ; } int i = 0; for (ServerName addr : serverSet){ if ((i++) % 3 == 0) { System.out.print("\n\t\t\t"); } System.out.print(addr.getHostAndPort() + " ; "); } System.out.println("\n"); } }