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.favored;
020
021import java.util.ArrayList;
022import java.util.List;
023import java.util.Map;
024import java.util.concurrent.ConcurrentHashMap;
025import java.util.stream.Collectors;
026import org.apache.hadoop.hbase.ServerName;
027import org.apache.hadoop.hbase.client.RegionInfo;
028import org.apache.yetus.audience.InterfaceAudience;
029
030/**
031 * This class contains the mapping information between each region name and
032 * its favored region server list. Used by {@link FavoredNodeLoadBalancer} set
033 * of classes and from unit tests (hence the class is public)
034 *
035 * All the access to this class is thread-safe.
036 */
037@InterfaceAudience.Private
038public class FavoredNodesPlan {
039  /** The map between each region name and its favored region server list */
040  private final Map<String, List<ServerName>> favoredNodesMap;
041
042  public static enum Position {
043    PRIMARY,
044    SECONDARY,
045    TERTIARY
046  }
047
048  public FavoredNodesPlan() {
049    this.favoredNodesMap = new ConcurrentHashMap<>();
050  }
051
052  /**
053   * Add to existing Map of FavoredNodes.
054   */
055  void updateFavoredNodesMap(FavoredNodesPlan fnp) {
056    this.favoredNodesMap.putAll(fnp.favoredNodesMap);
057  }
058
059  /**
060   * Update an assignment to the plan
061   */
062  public void updateFavoredNodesMap(RegionInfo region, List<ServerName> servers) {
063    if (region == null || servers == null || servers.isEmpty()) {
064      return;
065    }
066    this.favoredNodesMap.put(region.getRegionNameAsString(), servers);
067  }
068
069  /**
070   * Remove a favored node assignment
071   * @return the list of favored region server for this region based on the plan
072   */
073  List<ServerName> removeFavoredNodes(RegionInfo region) {
074    return favoredNodesMap.remove(region.getRegionNameAsString());
075  }
076
077  /**
078   * @return the list of favored region server for this region based on the plan
079   */
080  public List<ServerName> getFavoredNodes(RegionInfo region) {
081    return favoredNodesMap.get(region.getRegionNameAsString());
082  }
083
084  /**
085   * Return the position of the server in the favoredNodes list. Assumes the
086   * favoredNodes list is of size 3.
087   * @return position
088   */
089  public static Position getFavoredServerPosition(
090      List<ServerName> favoredNodes, ServerName server) {
091    if (favoredNodes == null || server == null ||
092        favoredNodes.size() != FavoredNodeAssignmentHelper.FAVORED_NODES_NUM) {
093      return null;
094    }
095    for (Position p : Position.values()) {
096      if (ServerName.isSameAddress(favoredNodes.get(p.ordinal()),server)) {
097        return p;
098      }
099    }
100    return null;
101  }
102
103  /**
104   * @return the mapping between each region to its favored region server list
105   */
106  public Map<String, List<ServerName>> getAssignmentMap() {
107    // Make a deep copy so changes don't harm our copy of favoredNodesMap.
108    return this.favoredNodesMap.entrySet().stream().
109      collect(Collectors.toMap(k -> k.getKey(), v -> new ArrayList<ServerName>(v.getValue())));
110  }
111
112  public int size() {
113    return this.favoredNodesMap.size();
114  }
115
116  @Override
117  public boolean equals(Object o) {
118    if (this == o) {
119      return true;
120    }
121    if (o == null) {
122      return false;
123    }
124    if (getClass() != o.getClass()) {
125      return false;
126    }
127    // To compare the map from object o is identical to current assignment map.
128    Map<String, List<ServerName>> comparedMap = ((FavoredNodesPlan)o).favoredNodesMap;
129
130    // compare the size
131    if (comparedMap.size() != this.favoredNodesMap.size()) {
132      return false;
133    }
134
135    // compare each element in the assignment map
136    for (Map.Entry<String, List<ServerName>> entry :
137      comparedMap.entrySet()) {
138      List<ServerName> serverList = this.favoredNodesMap.get(entry.getKey());
139      if (serverList == null && entry.getValue() != null) {
140        return false;
141      } else if (serverList != null && !serverList.equals(entry.getValue())) {
142        return false;
143      }
144    }
145    return true;
146  }
147
148  @Override
149  public int hashCode() {
150    return favoredNodesMap.hashCode();
151  }
152}