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;
019
020import static org.apache.hadoop.hbase.favored.FavoredNodesPlan.Position.PRIMARY;
021import static org.apache.hadoop.hbase.favored.FavoredNodesPlan.Position.SECONDARY;
022import static org.apache.hadoop.hbase.favored.FavoredNodesPlan.Position.TERTIARY;
023
024import java.io.IOException;
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.HashMap;
028import java.util.HashSet;
029import java.util.List;
030import java.util.Map;
031import java.util.Set;
032import java.util.TreeMap;
033import org.apache.hadoop.hbase.CatalogFamilyFormat;
034import org.apache.hadoop.hbase.HConstants;
035import org.apache.hadoop.hbase.HRegionLocation;
036import org.apache.hadoop.hbase.RegionLocations;
037import org.apache.hadoop.hbase.ServerName;
038import org.apache.hadoop.hbase.TableName;
039import org.apache.hadoop.hbase.client.Connection;
040import org.apache.hadoop.hbase.client.RegionInfo;
041import org.apache.hadoop.hbase.client.Result;
042import org.apache.hadoop.hbase.client.ResultScanner;
043import org.apache.hadoop.hbase.client.Table;
044import org.apache.hadoop.hbase.favored.FavoredNodeAssignmentHelper;
045import org.apache.hadoop.hbase.favored.FavoredNodesPlan;
046import org.apache.yetus.audience.InterfaceAudience;
047import org.slf4j.Logger;
048import org.slf4j.LoggerFactory;
049
050/**
051 * Used internally for reading meta and constructing datastructures that are then queried, for
052 * things like regions to regionservers, table to regions, etc. It also records the favored nodes
053 * mapping for regions.
054 */
055@InterfaceAudience.Private
056public class SnapshotOfRegionAssignmentFromMeta {
057  private static final Logger LOG =
058    LoggerFactory.getLogger(SnapshotOfRegionAssignmentFromMeta.class.getName());
059
060  private final Connection connection;
061
062  /** the table name to region map */
063  private final Map<TableName, List<RegionInfo>> tableToRegionMap;
064  /** the region to region server map */
065  // private final Map<RegionInfo, ServerName> regionToRegionServerMap;
066  private Map<RegionInfo, ServerName> regionToRegionServerMap;
067  /** the region name to region info map */
068  private final Map<String, RegionInfo> regionNameToRegionInfoMap;
069
070  /** the regionServer to region map */
071  private final Map<ServerName, List<RegionInfo>> currentRSToRegionMap;
072  private final Map<ServerName, List<RegionInfo>> secondaryRSToRegionMap;
073  private final Map<ServerName, List<RegionInfo>> teritiaryRSToRegionMap;
074  private final Map<ServerName, List<RegionInfo>> primaryRSToRegionMap;
075  /** the existing assignment plan in the hbase:meta region */
076  private final FavoredNodesPlan existingAssignmentPlan;
077  private final Set<TableName> disabledTables;
078  private final boolean excludeOfflinedSplitParents;
079
080  public SnapshotOfRegionAssignmentFromMeta(Connection connection) {
081    this(connection, new HashSet<>(), false);
082  }
083
084  public SnapshotOfRegionAssignmentFromMeta(Connection connection, Set<TableName> disabledTables,
085    boolean excludeOfflinedSplitParents) {
086    this.connection = connection;
087    tableToRegionMap = new HashMap<>();
088    regionToRegionServerMap = new HashMap<>();
089    currentRSToRegionMap = new HashMap<>();
090    primaryRSToRegionMap = new HashMap<>();
091    secondaryRSToRegionMap = new HashMap<>();
092    teritiaryRSToRegionMap = new HashMap<>();
093    regionNameToRegionInfoMap = new TreeMap<>();
094    existingAssignmentPlan = new FavoredNodesPlan();
095    this.disabledTables = disabledTables;
096    this.excludeOfflinedSplitParents = excludeOfflinedSplitParents;
097  }
098
099  private void processMetaRecord(Result result) throws IOException {
100    if (result == null || result.isEmpty()) {
101      return;
102    }
103    RegionLocations rl = CatalogFamilyFormat.getRegionLocations(result);
104    if (rl == null) {
105      return;
106    }
107    RegionInfo hri = rl.getRegionLocation(0).getRegion();
108    if (hri == null) {
109      return;
110    }
111    if (hri.getTable() == null) {
112      return;
113    }
114    if (disabledTables.contains(hri.getTable())) {
115      return;
116    }
117    // Are we to include split parents in the list?
118    if (excludeOfflinedSplitParents && hri.isSplit()) {
119      return;
120    }
121    HRegionLocation[] hrls = rl.getRegionLocations();
122
123    // Add the current assignment to the snapshot for all replicas
124    for (int i = 0; i < hrls.length; i++) {
125      if (hrls[i] == null) {
126        continue;
127      }
128      hri = hrls[i].getRegion();
129      if (hri == null) {
130        continue;
131      }
132      addAssignment(hri, hrls[i].getServerName());
133      addRegion(hri);
134    }
135
136    hri = rl.getRegionLocation(0).getRegion();
137    // the code below is to handle favored nodes
138    byte[] favoredNodes = result.getValue(HConstants.CATALOG_FAMILY,
139      FavoredNodeAssignmentHelper.FAVOREDNODES_QUALIFIER);
140    if (favoredNodes == null) {
141      return;
142    }
143    // Add the favored nodes into assignment plan
144    ServerName[] favoredServerList = FavoredNodeAssignmentHelper.getFavoredNodesList(favoredNodes);
145    // Add the favored nodes into assignment plan
146    existingAssignmentPlan.updateFavoredNodesMap(hri, Arrays.asList(favoredServerList));
147
148    /*
149     * Typically there should be FAVORED_NODES_NUM favored nodes for a region in meta. If there is
150     * less than FAVORED_NODES_NUM, lets use as much as we can but log a warning.
151     */
152    if (favoredServerList.length != FavoredNodeAssignmentHelper.FAVORED_NODES_NUM) {
153      LOG.warn("Insufficient favored nodes for region " + hri + " fn: "
154        + Arrays.toString(favoredServerList));
155    }
156    for (int i = 0; i < favoredServerList.length; i++) {
157      if (i == PRIMARY.ordinal()) {
158        addPrimaryAssignment(hri, favoredServerList[i]);
159      }
160      if (i == SECONDARY.ordinal()) {
161        addSecondaryAssignment(hri, favoredServerList[i]);
162      }
163      if (i == TERTIARY.ordinal()) {
164        addTeritiaryAssignment(hri, favoredServerList[i]);
165      }
166    }
167  }
168
169  /**
170   * Initialize the region assignment snapshot by scanning the hbase:meta table
171   */
172  public void initialize() throws IOException {
173    LOG.info("Start to scan {} for the current region assignment snapshot",
174      TableName.META_TABLE_NAME);
175    // Scan hbase:meta to pick up user regions
176    try (Table metaTable = connection.getTable(TableName.META_TABLE_NAME);
177      ResultScanner scanner = metaTable.getScanner(HConstants.CATALOG_FAMILY)) {
178      for (;;) {
179        Result result = scanner.next();
180        if (result == null) {
181          break;
182        }
183        try {
184          processMetaRecord(result);
185        } catch (RuntimeException e) {
186          LOG.error("Catch remote exception " + e.getMessage() + " when processing" + result);
187          throw e;
188        }
189      }
190    }
191    LOG.info("Finished scanning {} for the current region assignment snapshot",
192      TableName.META_TABLE_NAME);
193  }
194
195  private void addRegion(RegionInfo regionInfo) {
196    // Process the region name to region info map
197    regionNameToRegionInfoMap.put(regionInfo.getRegionNameAsString(), regionInfo);
198
199    // Process the table to region map
200    TableName tableName = regionInfo.getTable();
201    List<RegionInfo> regionList = tableToRegionMap.get(tableName);
202    if (regionList == null) {
203      regionList = new ArrayList<>();
204    }
205    // Add the current region info into the tableToRegionMap
206    regionList.add(regionInfo);
207    tableToRegionMap.put(tableName, regionList);
208  }
209
210  private void addAssignment(RegionInfo regionInfo, ServerName server) {
211    // Process the region to region server map
212    regionToRegionServerMap.put(regionInfo, server);
213
214    if (server == null) return;
215
216    // Process the region server to region map
217    List<RegionInfo> regionList = currentRSToRegionMap.get(server);
218    if (regionList == null) {
219      regionList = new ArrayList<>();
220    }
221    regionList.add(regionInfo);
222    currentRSToRegionMap.put(server, regionList);
223  }
224
225  private void addPrimaryAssignment(RegionInfo regionInfo, ServerName server) {
226    // Process the region server to region map
227    List<RegionInfo> regionList = primaryRSToRegionMap.get(server);
228    if (regionList == null) {
229      regionList = new ArrayList<>();
230    }
231    regionList.add(regionInfo);
232    primaryRSToRegionMap.put(server, regionList);
233  }
234
235  private void addSecondaryAssignment(RegionInfo regionInfo, ServerName server) {
236    // Process the region server to region map
237    List<RegionInfo> regionList = secondaryRSToRegionMap.get(server);
238    if (regionList == null) {
239      regionList = new ArrayList<>();
240    }
241    regionList.add(regionInfo);
242    secondaryRSToRegionMap.put(server, regionList);
243  }
244
245  private void addTeritiaryAssignment(RegionInfo regionInfo, ServerName server) {
246    // Process the region server to region map
247    List<RegionInfo> regionList = teritiaryRSToRegionMap.get(server);
248    if (regionList == null) {
249      regionList = new ArrayList<>();
250    }
251    regionList.add(regionInfo);
252    teritiaryRSToRegionMap.put(server, regionList);
253  }
254
255  /**
256   * Get the regioninfo for a region
257   * @return the regioninfo
258   */
259  public Map<String, RegionInfo> getRegionNameToRegionInfoMap() {
260    return this.regionNameToRegionInfoMap;
261  }
262
263  /**
264   * Get regions for tables
265   * @return a mapping from table to regions
266   */
267  public Map<TableName, List<RegionInfo>> getTableToRegionMap() {
268    return tableToRegionMap;
269  }
270
271  /**
272   * Get region to region server map
273   * @return region to region server map
274   */
275  public Map<RegionInfo, ServerName> getRegionToRegionServerMap() {
276    return regionToRegionServerMap;
277  }
278
279  /**
280   * Get regionserver to region map
281   * @return regionserver to region map
282   */
283  public Map<ServerName, List<RegionInfo>> getRegionServerToRegionMap() {
284    return currentRSToRegionMap;
285  }
286
287  /**
288   * Get the favored nodes plan
289   * @return the existing favored nodes plan
290   */
291  public FavoredNodesPlan getExistingAssignmentPlan() {
292    return this.existingAssignmentPlan;
293  }
294
295  /**
296   * Get the table set
297   * @return the table set
298   */
299  public Set<TableName> getTableSet() {
300    return this.tableToRegionMap.keySet();
301  }
302
303  public Map<ServerName, List<RegionInfo>> getSecondaryToRegionInfoMap() {
304    return this.secondaryRSToRegionMap;
305  }
306
307  public Map<ServerName, List<RegionInfo>> getTertiaryToRegionInfoMap() {
308    return this.teritiaryRSToRegionMap;
309  }
310
311  public Map<ServerName, List<RegionInfo>> getPrimaryToRegionInfoMap() {
312    return this.primaryRSToRegionMap;
313  }
314}