001/**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019package org.apache.hadoop.hbase.master;
020
021import java.text.DecimalFormat;
022import java.util.ArrayList;
023import java.util.HashMap;
024import java.util.HashSet;
025import java.util.List;
026import java.util.Map;
027import java.util.Set;
028
029import org.apache.hadoop.hbase.ServerName;
030import org.apache.hadoop.hbase.TableName;
031import org.apache.hadoop.hbase.client.RegionInfo;
032import org.apache.hadoop.hbase.favored.FavoredNodeAssignmentHelper;
033import org.apache.hadoop.hbase.favored.FavoredNodesPlan;
034import org.apache.yetus.audience.InterfaceAudience;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037/**
038 * Helper class that is used by {@link RegionPlacementMaintainer} to print
039 * information for favored nodes
040 *
041 */
042@InterfaceAudience.Private
043public class AssignmentVerificationReport {
044  private static final Logger LOG = LoggerFactory.getLogger(
045      AssignmentVerificationReport.class.getName());
046
047  private TableName tableName = null;
048  private boolean enforceLocality = false;
049  private boolean isFilledUp = false;
050
051  private int totalRegions = 0;
052  private int totalRegionServers = 0;
053  // for unassigned regions
054  private List<RegionInfo> unAssignedRegionsList = new ArrayList<>();
055
056  // For regions without valid favored nodes
057  private List<RegionInfo> regionsWithoutValidFavoredNodes = new ArrayList<>();
058
059  // For regions not running on the favored nodes
060  private List<RegionInfo> nonFavoredAssignedRegionList = new ArrayList<>();
061
062  // For regions running on the favored nodes
063  private int totalFavoredAssignments = 0;
064  private int[] favoredNodes = new int[FavoredNodeAssignmentHelper.FAVORED_NODES_NUM];
065  private float[] favoredNodesLocalitySummary =
066      new float[FavoredNodeAssignmentHelper.FAVORED_NODES_NUM];
067  private float actualLocalitySummary = 0;
068
069  // For region balancing information
070  private float avgRegionsOnRS = 0;
071  private int maxRegionsOnRS = 0;
072  private int minRegionsOnRS = Integer.MAX_VALUE;
073  private Set<ServerName> mostLoadedRSSet = new HashSet<>();
074  private Set<ServerName> leastLoadedRSSet = new HashSet<>();
075
076  private float avgDispersionScore = 0;
077  private float maxDispersionScore = 0;
078  private Set<ServerName> maxDispersionScoreServerSet = new HashSet<>();
079  private float minDispersionScore = Float.MAX_VALUE;
080  private Set<ServerName> minDispersionScoreServerSet = new HashSet<>();
081
082  private float avgDispersionNum = 0;
083  private float maxDispersionNum = 0;
084  private Set<ServerName> maxDispersionNumServerSet = new HashSet<>();
085  private float minDispersionNum = Float.MAX_VALUE;
086  private Set<ServerName> minDispersionNumServerSet = new HashSet<>();
087
088  public void fillUp(TableName tableName, SnapshotOfRegionAssignmentFromMeta snapshot,
089      Map<String, Map<String, Float>> regionLocalityMap) {
090    // Set the table name
091    this.tableName = tableName;
092
093    // Get all the regions for this table
094    List<RegionInfo> regionInfoList =
095      snapshot.getTableToRegionMap().get(tableName);
096    // Get the total region num for the current table
097    this.totalRegions = regionInfoList.size();
098
099    // Get the existing assignment plan
100    FavoredNodesPlan favoredNodesAssignment = snapshot.getExistingAssignmentPlan();
101    // Get the region to region server mapping
102    Map<RegionInfo, ServerName> currentAssignment =
103      snapshot.getRegionToRegionServerMap();
104    // Initialize the server to its hosing region counter map
105    Map<ServerName, Integer> serverToHostingRegionCounterMap = new HashMap<>();
106
107    Map<ServerName, Integer> primaryRSToRegionCounterMap = new HashMap<>();
108    Map<ServerName, Set<ServerName>> primaryToSecTerRSMap = new HashMap<>();
109
110    // Check the favored nodes and its locality information
111    // Also keep tracker of the most loaded and least loaded region servers
112    for (RegionInfo region : regionInfoList) {
113      try {
114        ServerName currentRS = currentAssignment.get(region);
115        // Handle unassigned regions
116        if (currentRS == null) {
117          unAssignedRegionsList.add(region);
118          continue;
119        }
120
121        // Keep updating the server to is hosting region counter map
122        Integer hostRegionCounter = serverToHostingRegionCounterMap.get(currentRS);
123        if (hostRegionCounter == null) {
124          hostRegionCounter = Integer.valueOf(0);
125        }
126        hostRegionCounter = hostRegionCounter.intValue() + 1;
127        serverToHostingRegionCounterMap.put(currentRS, hostRegionCounter);
128
129        // Get the favored nodes from the assignment plan and verify it.
130        List<ServerName> favoredNodes = favoredNodesAssignment.getFavoredNodes(region);
131        if (favoredNodes == null ||
132            favoredNodes.size() != FavoredNodeAssignmentHelper.FAVORED_NODES_NUM) {
133          regionsWithoutValidFavoredNodes.add(region);
134          continue;
135        }
136        // Get the primary, secondary and tertiary region server
137        ServerName primaryRS =
138          favoredNodes.get(FavoredNodesPlan.Position.PRIMARY.ordinal());
139        ServerName secondaryRS =
140          favoredNodes.get(FavoredNodesPlan.Position.SECONDARY.ordinal());
141        ServerName tertiaryRS =
142          favoredNodes.get(FavoredNodesPlan.Position.TERTIARY.ordinal());
143
144        // Update the primary rs to its region set map
145        Integer regionCounter = primaryRSToRegionCounterMap.get(primaryRS);
146        if (regionCounter == null) {
147          regionCounter = Integer.valueOf(0);
148        }
149        regionCounter = regionCounter.intValue() + 1;
150        primaryRSToRegionCounterMap.put(primaryRS, regionCounter);
151
152        // Update the primary rs to secondary and tertiary rs map
153        Set<ServerName> secAndTerSet = primaryToSecTerRSMap.get(primaryRS);
154        if (secAndTerSet == null) {
155          secAndTerSet = new HashSet<>();
156        }
157        secAndTerSet.add(secondaryRS);
158        secAndTerSet.add(tertiaryRS);
159        primaryToSecTerRSMap.put(primaryRS, secAndTerSet);
160
161        // Get the position of the current region server in the favored nodes list
162        FavoredNodesPlan.Position favoredNodePosition =
163          FavoredNodesPlan.getFavoredServerPosition(favoredNodes, currentRS);
164
165        // Handle the non favored assignment.
166        if (favoredNodePosition == null) {
167          nonFavoredAssignedRegionList.add(region);
168          continue;
169        }
170        // Increase the favored nodes assignment.
171        this.favoredNodes[favoredNodePosition.ordinal()]++;
172        totalFavoredAssignments++;
173
174        // Summary the locality information for each favored nodes
175        if (regionLocalityMap != null) {
176          // Set the enforce locality as true;
177          this.enforceLocality = true;
178
179          // Get the region degree locality map
180          Map<String, Float> regionDegreeLocalityMap =
181            regionLocalityMap.get(region.getEncodedName());
182          if (regionDegreeLocalityMap == null) {
183            continue; // ignore the region which doesn't have any store files.
184          }
185
186          // Get the locality summary for each favored nodes
187          for (FavoredNodesPlan.Position p : FavoredNodesPlan.Position.values()) {
188            ServerName favoredNode = favoredNodes.get(p.ordinal());
189            // Get the locality for the current favored nodes
190            Float locality =
191              regionDegreeLocalityMap.get(favoredNode.getHostname());
192            if (locality != null) {
193              this.favoredNodesLocalitySummary[p.ordinal()] += locality;
194            }
195          }
196
197          // Get the locality summary for the current region server
198          Float actualLocality =
199            regionDegreeLocalityMap.get(currentRS.getHostname());
200          if (actualLocality != null) {
201            this.actualLocalitySummary += actualLocality;
202          }
203        }
204      } catch (Exception e) {
205        LOG.error("Cannot verify the region assignment for region " +
206            ((region == null) ? " null " : region.getRegionNameAsString()) +
207            "because of " + e);
208      }
209    }
210
211    float dispersionScoreSummary = 0;
212    float dispersionNumSummary = 0;
213    // Calculate the secondary score for each primary region server
214    for (Map.Entry<ServerName, Integer> entry :
215      primaryRSToRegionCounterMap.entrySet()) {
216      ServerName primaryRS = entry.getKey();
217      Integer regionsOnPrimary = entry.getValue();
218
219      // Process the dispersion number and score
220      float dispersionScore = 0;
221      int dispersionNum = 0;
222      if (primaryToSecTerRSMap.get(primaryRS) != null
223          && regionsOnPrimary.intValue() != 0) {
224        dispersionNum = primaryToSecTerRSMap.get(primaryRS).size();
225        dispersionScore = dispersionNum /
226          ((float) regionsOnPrimary.intValue() * 2);
227      }
228      // Update the max dispersion score
229      if (dispersionScore > this.maxDispersionScore) {
230        this.maxDispersionScoreServerSet.clear();
231        this.maxDispersionScoreServerSet.add(primaryRS);
232        this.maxDispersionScore = dispersionScore;
233      } else if (dispersionScore == this.maxDispersionScore) {
234        this.maxDispersionScoreServerSet.add(primaryRS);
235      }
236
237      // Update the max dispersion num
238      if (dispersionNum > this.maxDispersionNum) {
239        this.maxDispersionNumServerSet.clear();
240        this.maxDispersionNumServerSet.add(primaryRS);
241        this.maxDispersionNum = dispersionNum;
242      } else if (dispersionNum == this.maxDispersionNum) {
243        this.maxDispersionNumServerSet.add(primaryRS);
244      }
245
246      // Update the min dispersion score
247      if (dispersionScore < this.minDispersionScore) {
248        this.minDispersionScoreServerSet.clear();
249        this.minDispersionScoreServerSet.add(primaryRS);
250        this.minDispersionScore = dispersionScore;
251      } else if (dispersionScore == this.minDispersionScore) {
252        this.minDispersionScoreServerSet.add(primaryRS);
253      }
254
255      // Update the min dispersion num
256      if (dispersionNum < this.minDispersionNum) {
257        this.minDispersionNumServerSet.clear();
258        this.minDispersionNumServerSet.add(primaryRS);
259        this.minDispersionNum = dispersionNum;
260      } else if (dispersionNum == this.minDispersionNum) {
261        this.minDispersionNumServerSet.add(primaryRS);
262      }
263
264      dispersionScoreSummary += dispersionScore;
265      dispersionNumSummary += dispersionNum;
266    }
267
268    // Update the avg dispersion score
269    if (primaryRSToRegionCounterMap.keySet().size() != 0) {
270      this.avgDispersionScore = dispersionScoreSummary /
271         (float) primaryRSToRegionCounterMap.keySet().size();
272      this.avgDispersionNum = dispersionNumSummary /
273         (float) primaryRSToRegionCounterMap.keySet().size();
274    }
275
276    // Fill up the most loaded and least loaded region server information
277    for (Map.Entry<ServerName, Integer> entry :
278      serverToHostingRegionCounterMap.entrySet()) {
279      ServerName currentRS = entry.getKey();
280      int hostRegionCounter = entry.getValue().intValue();
281
282      // Update the most loaded region server list and maxRegionsOnRS
283      if (hostRegionCounter > this.maxRegionsOnRS) {
284        maxRegionsOnRS = hostRegionCounter;
285        this.mostLoadedRSSet.clear();
286        this.mostLoadedRSSet.add(currentRS);
287      } else if (hostRegionCounter == this.maxRegionsOnRS) {
288        this.mostLoadedRSSet.add(currentRS);
289      }
290
291      // Update the least loaded region server list and minRegionsOnRS
292      if (hostRegionCounter < this.minRegionsOnRS) {
293        this.minRegionsOnRS = hostRegionCounter;
294        this.leastLoadedRSSet.clear();
295        this.leastLoadedRSSet.add(currentRS);
296      } else if (hostRegionCounter == this.minRegionsOnRS) {
297        this.leastLoadedRSSet.add(currentRS);
298      }
299    }
300
301    // and total region servers
302    this.totalRegionServers = serverToHostingRegionCounterMap.keySet().size();
303    this.avgRegionsOnRS = (totalRegionServers == 0) ? 0 :
304      (totalRegions / (float) totalRegionServers);
305    // Set the isFilledUp as true
306    isFilledUp = true;
307  }
308
309  /**
310   * Use this to project the dispersion scores
311   * @param tableName
312   * @param snapshot
313   * @param newPlan
314   */
315  public void fillUpDispersion(TableName tableName,
316      SnapshotOfRegionAssignmentFromMeta snapshot, FavoredNodesPlan newPlan) {
317    // Set the table name
318    this.tableName = tableName;
319    // Get all the regions for this table
320    List<RegionInfo> regionInfoList = snapshot.getTableToRegionMap().get(
321        tableName);
322    // Get the total region num for the current table
323    this.totalRegions = regionInfoList.size();
324    FavoredNodesPlan plan = null;
325    if (newPlan == null) {
326      plan = snapshot.getExistingAssignmentPlan();
327    } else {
328      plan = newPlan;
329    }
330    // Get the region to region server mapping
331    Map<ServerName, Integer> primaryRSToRegionCounterMap = new HashMap<>();
332    Map<ServerName, Set<ServerName>> primaryToSecTerRSMap = new HashMap<>();
333
334    // Check the favored nodes and its locality information
335    // Also keep tracker of the most loaded and least loaded region servers
336    for (RegionInfo region : regionInfoList) {
337      try {
338        // Get the favored nodes from the assignment plan and verify it.
339        List<ServerName> favoredNodes = plan.getFavoredNodes(region);
340        if (favoredNodes == null
341            || favoredNodes.size() != FavoredNodeAssignmentHelper.FAVORED_NODES_NUM) {
342          regionsWithoutValidFavoredNodes.add(region);
343          continue;
344        }
345        // Get the primary, secondary and tertiary region server
346        ServerName primaryRS = favoredNodes
347            .get(FavoredNodesPlan.Position.PRIMARY.ordinal());
348        ServerName secondaryRS = favoredNodes
349            .get(FavoredNodesPlan.Position.SECONDARY.ordinal());
350        ServerName tertiaryRS = favoredNodes
351            .get(FavoredNodesPlan.Position.TERTIARY.ordinal());
352
353        // Update the primary rs to its region set map
354        Integer regionCounter = primaryRSToRegionCounterMap.get(primaryRS);
355        if (regionCounter == null) {
356          regionCounter = Integer.valueOf(0);
357        }
358        regionCounter = regionCounter.intValue() + 1;
359        primaryRSToRegionCounterMap.put(primaryRS, regionCounter);
360
361        // Update the primary rs to secondary and tertiary rs map
362        Set<ServerName> secAndTerSet = primaryToSecTerRSMap.get(primaryRS);
363        if (secAndTerSet == null) {
364          secAndTerSet = new HashSet<>();
365        }
366        secAndTerSet.add(secondaryRS);
367        secAndTerSet.add(tertiaryRS);
368        primaryToSecTerRSMap.put(primaryRS, secAndTerSet);
369      } catch (Exception e) {
370        LOG.error("Cannot verify the region assignment for region "
371            + ((region == null) ? " null " : region.getRegionNameAsString())
372            + "because of " + e);
373      }
374    }
375    float dispersionScoreSummary = 0;
376    float dispersionNumSummary = 0;
377    // Calculate the secondary score for each primary region server
378    for (Map.Entry<ServerName, Integer> entry :
379      primaryRSToRegionCounterMap.entrySet()) {
380      ServerName primaryRS = entry.getKey();
381      Integer regionsOnPrimary = entry.getValue();
382
383      // Process the dispersion number and score
384      float dispersionScore = 0;
385      int dispersionNum = 0;
386      if (primaryToSecTerRSMap.get(primaryRS) != null
387          && regionsOnPrimary.intValue() != 0) {
388        dispersionNum = primaryToSecTerRSMap.get(primaryRS).size();
389        dispersionScore = dispersionNum /
390          ((float) regionsOnPrimary.intValue() * 2);
391      }
392
393      // Update the max dispersion num
394      if (dispersionNum > this.maxDispersionNum) {
395        this.maxDispersionNumServerSet.clear();
396        this.maxDispersionNumServerSet.add(primaryRS);
397        this.maxDispersionNum = dispersionNum;
398      } else if (dispersionNum == this.maxDispersionNum) {
399        this.maxDispersionNumServerSet.add(primaryRS);
400      }
401
402      // Update the min dispersion score
403      if (dispersionScore < this.minDispersionScore) {
404        this.minDispersionScoreServerSet.clear();
405        this.minDispersionScoreServerSet.add(primaryRS);
406        this.minDispersionScore = dispersionScore;
407      } else if (dispersionScore == this.minDispersionScore) {
408        this.minDispersionScoreServerSet.add(primaryRS);
409      }
410
411      // Update the min dispersion num
412      if (dispersionNum < this.minDispersionNum) {
413        this.minDispersionNumServerSet.clear();
414        this.minDispersionNumServerSet.add(primaryRS);
415        this.minDispersionNum = dispersionNum;
416      } else if (dispersionNum == this.minDispersionNum) {
417        this.minDispersionNumServerSet.add(primaryRS);
418      }
419
420      dispersionScoreSummary += dispersionScore;
421      dispersionNumSummary += dispersionNum;
422    }
423
424    // Update the avg dispersion score
425    if (primaryRSToRegionCounterMap.keySet().size() != 0) {
426      this.avgDispersionScore = dispersionScoreSummary /
427         (float) primaryRSToRegionCounterMap.keySet().size();
428      this.avgDispersionNum = dispersionNumSummary /
429         (float) primaryRSToRegionCounterMap.keySet().size();
430    }
431  }
432
433  /**
434   * @return list which contains just 3 elements: average dispersion score, max
435   * dispersion score and min dispersion score as first, second and third element
436   * respectively.
437   *
438   */
439  public List<Float> getDispersionInformation() {
440    List<Float> dispersion = new ArrayList<>();
441    dispersion.add(avgDispersionScore);
442    dispersion.add(maxDispersionScore);
443    dispersion.add(minDispersionScore);
444    return dispersion;
445  }
446
447  public void print(boolean isDetailMode) {
448    if (!isFilledUp) {
449      System.err.println("[Error] Region assignment verification report" +
450          "hasn't been filled up");
451    }
452    DecimalFormat df = new java.text.DecimalFormat( "#.##");
453
454    // Print some basic information
455    System.out.println("Region Assignment Verification for Table: " + tableName +
456        "\n\tTotal regions : " + totalRegions);
457
458    // Print the number of regions on each kinds of the favored nodes
459    System.out.println("\tTotal regions on favored nodes " +
460        totalFavoredAssignments);
461    for (FavoredNodesPlan.Position p : FavoredNodesPlan.Position.values()) {
462      System.out.println("\t\tTotal regions on "+ p.toString() +
463          " region servers: " + favoredNodes[p.ordinal()]);
464    }
465    // Print the number of regions in each kinds of invalid assignment
466    System.out.println("\tTotal unassigned regions: " +
467        unAssignedRegionsList.size());
468    if (isDetailMode) {
469      for (RegionInfo region : unAssignedRegionsList) {
470        System.out.println("\t\t" + region.getRegionNameAsString());
471      }
472    }
473
474    System.out.println("\tTotal regions NOT on favored nodes: " +
475        nonFavoredAssignedRegionList.size());
476    if (isDetailMode) {
477      for (RegionInfo region : nonFavoredAssignedRegionList) {
478        System.out.println("\t\t" + region.getRegionNameAsString());
479      }
480    }
481
482    System.out.println("\tTotal regions without favored nodes: " +
483        regionsWithoutValidFavoredNodes.size());
484    if (isDetailMode) {
485      for (RegionInfo region : regionsWithoutValidFavoredNodes) {
486        System.out.println("\t\t" + region.getRegionNameAsString());
487      }
488    }
489
490    // Print the locality information if enabled
491    if (this.enforceLocality && totalRegions != 0) {
492      // Print the actual locality for this table
493      float actualLocality = 100 *
494        this.actualLocalitySummary / (float) totalRegions;
495      System.out.println("\n\tThe actual avg locality is " +
496          df.format(actualLocality) + " %");
497
498      // Print the expected locality if regions are placed on the each kinds of
499      // favored nodes
500      for (FavoredNodesPlan.Position p : FavoredNodesPlan.Position.values()) {
501        float avgLocality = 100 *
502          (favoredNodesLocalitySummary[p.ordinal()] / (float) totalRegions);
503        System.out.println("\t\tThe expected avg locality if all regions" +
504            " on the " + p.toString() + " region servers: "
505            + df.format(avgLocality) + " %");
506      }
507    }
508
509    // Print the region balancing information
510    System.out.println("\n\tTotal hosting region servers: " +
511        totalRegionServers);
512    // Print the region balance information
513    if (totalRegionServers != 0) {
514      System.out.println(
515          "\tAvg dispersion num: " +df.format(avgDispersionNum) +
516          " hosts;\tMax dispersion num: " + df.format(maxDispersionNum) +
517          " hosts;\tMin dispersion num: " + df.format(minDispersionNum) +
518          " hosts;");
519
520      System.out.println("\t\tThe number of the region servers with the max" +
521          " dispersion num: " + this.maxDispersionNumServerSet.size());
522      if (isDetailMode) {
523        printHServerAddressSet(maxDispersionNumServerSet);
524      }
525
526      System.out.println("\t\tThe number of the region servers with the min" +
527          " dispersion num: " + this.minDispersionNumServerSet.size());
528      if (isDetailMode) {
529        printHServerAddressSet(maxDispersionNumServerSet);
530      }
531
532      System.out.println(
533          "\tAvg dispersion score: " + df.format(avgDispersionScore) +
534          ";\tMax dispersion score: " + df.format(maxDispersionScore) +
535          ";\tMin dispersion score: " + df.format(minDispersionScore) + ";");
536
537      System.out.println("\t\tThe number of the region servers with the max" +
538          " dispersion score: " + this.maxDispersionScoreServerSet.size());
539      if (isDetailMode) {
540        printHServerAddressSet(maxDispersionScoreServerSet);
541      }
542
543      System.out.println("\t\tThe number of the region servers with the min" +
544          " dispersion score: " + this.minDispersionScoreServerSet.size());
545      if (isDetailMode) {
546        printHServerAddressSet(minDispersionScoreServerSet);
547      }
548
549      System.out.println(
550          "\tAvg regions/region server: " + df.format(avgRegionsOnRS) +
551          ";\tMax regions/region server: " + maxRegionsOnRS +
552          ";\tMin regions/region server: " + minRegionsOnRS + ";");
553
554      // Print the details about the most loaded region servers
555      System.out.println("\t\tThe number of the most loaded region servers: "
556          + mostLoadedRSSet.size());
557      if (isDetailMode) {
558        printHServerAddressSet(mostLoadedRSSet);
559      }
560
561      // Print the details about the least loaded region servers
562      System.out.println("\t\tThe number of the least loaded region servers: "
563          + leastLoadedRSSet.size());
564      if (isDetailMode) {
565        printHServerAddressSet(leastLoadedRSSet);
566      }
567    }
568    System.out.println("==============================");
569  }
570
571  /**
572   * Return the unassigned regions
573   * @return unassigned regions
574   */
575  List<RegionInfo> getUnassignedRegions() {
576    return unAssignedRegionsList;
577  }
578
579  /**
580   * Return the regions without favored nodes
581   * @return regions without favored nodes
582   */
583  List<RegionInfo> getRegionsWithoutValidFavoredNodes() {
584    return regionsWithoutValidFavoredNodes;
585  }
586
587  /**
588   * Return the regions not assigned to its favored nodes
589   * @return regions not assigned to its favored nodes
590   */
591  List<RegionInfo> getNonFavoredAssignedRegions() {
592    return nonFavoredAssignedRegionList;
593  }
594
595  /**
596   * Return the number of regions assigned to their favored nodes
597   * @return number of regions assigned to their favored nodes
598   */
599  int getTotalFavoredAssignments() {
600    return totalFavoredAssignments;
601  }
602
603  /**
604   * Return the number of regions based on the position (primary/secondary/
605   * tertiary) assigned to their favored nodes
606   * @param position
607   * @return the number of regions
608   */
609  int getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position position) {
610    return favoredNodes[position.ordinal()];
611  }
612
613  private void printHServerAddressSet(Set<ServerName> serverSet) {
614    if (serverSet == null) {
615      return ;
616    }
617    int i = 0;
618    for (ServerName addr : serverSet){
619      if ((i++) % 3 == 0) {
620        System.out.print("\n\t\t\t");
621      }
622      System.out.print(addr.getHostAndPort() + " ; ");
623    }
624    System.out.println("\n");
625  }
626}