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.HConstants;
034import org.apache.hadoop.hbase.HRegionLocation;
035import org.apache.hadoop.hbase.MetaTableAccessor;
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 = MetaTableAccessor.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 the hbase:meta for the current region assignment " + "snappshot");
174    // Scan hbase:meta to pick up user regions
175    try (Table metaTable = connection.getTable(TableName.META_TABLE_NAME);
176      ResultScanner scanner = metaTable.getScanner(HConstants.CATALOG_FAMILY)) {
177      for (;;) {
178        Result result = scanner.next();
179        if (result == null) {
180          break;
181        }
182        try {
183          processMetaRecord(result);
184        } catch (RuntimeException e) {
185          LOG.error("Catch remote exception " + e.getMessage() + " when processing" + result);
186          throw e;
187        }
188      }
189    }
190    LOG.info("Finished to scan the hbase:meta for the current region assignment" + "snapshot");
191  }
192
193  private void addRegion(RegionInfo regionInfo) {
194    // Process the region name to region info map
195    regionNameToRegionInfoMap.put(regionInfo.getRegionNameAsString(), regionInfo);
196
197    // Process the table to region map
198    TableName tableName = regionInfo.getTable();
199    List<RegionInfo> regionList = tableToRegionMap.get(tableName);
200    if (regionList == null) {
201      regionList = new ArrayList<>();
202    }
203    // Add the current region info into the tableToRegionMap
204    regionList.add(regionInfo);
205    tableToRegionMap.put(tableName, regionList);
206  }
207
208  private void addAssignment(RegionInfo regionInfo, ServerName server) {
209    // Process the region to region server map
210    regionToRegionServerMap.put(regionInfo, server);
211
212    if (server == null) return;
213
214    // Process the region server to region map
215    List<RegionInfo> regionList = currentRSToRegionMap.get(server);
216    if (regionList == null) {
217      regionList = new ArrayList<>();
218    }
219    regionList.add(regionInfo);
220    currentRSToRegionMap.put(server, regionList);
221  }
222
223  private void addPrimaryAssignment(RegionInfo regionInfo, ServerName server) {
224    // Process the region server to region map
225    List<RegionInfo> regionList = primaryRSToRegionMap.get(server);
226    if (regionList == null) {
227      regionList = new ArrayList<>();
228    }
229    regionList.add(regionInfo);
230    primaryRSToRegionMap.put(server, regionList);
231  }
232
233  private void addSecondaryAssignment(RegionInfo regionInfo, ServerName server) {
234    // Process the region server to region map
235    List<RegionInfo> regionList = secondaryRSToRegionMap.get(server);
236    if (regionList == null) {
237      regionList = new ArrayList<>();
238    }
239    regionList.add(regionInfo);
240    secondaryRSToRegionMap.put(server, regionList);
241  }
242
243  private void addTeritiaryAssignment(RegionInfo regionInfo, ServerName server) {
244    // Process the region server to region map
245    List<RegionInfo> regionList = teritiaryRSToRegionMap.get(server);
246    if (regionList == null) {
247      regionList = new ArrayList<>();
248    }
249    regionList.add(regionInfo);
250    teritiaryRSToRegionMap.put(server, regionList);
251  }
252
253  /**
254   * Get the regioninfo for a region
255   * @return the regioninfo
256   */
257  public Map<String, RegionInfo> getRegionNameToRegionInfoMap() {
258    return this.regionNameToRegionInfoMap;
259  }
260
261  /**
262   * Get regions for tables
263   * @return a mapping from table to regions
264   */
265  public Map<TableName, List<RegionInfo>> getTableToRegionMap() {
266    return tableToRegionMap;
267  }
268
269  /**
270   * Get region to region server map
271   * @return region to region server map
272   */
273  public Map<RegionInfo, ServerName> getRegionToRegionServerMap() {
274    return regionToRegionServerMap;
275  }
276
277  /**
278   * Get regionserver to region map
279   * @return regionserver to region map
280   */
281  public Map<ServerName, List<RegionInfo>> getRegionServerToRegionMap() {
282    return currentRSToRegionMap;
283  }
284
285  /**
286   * Get the favored nodes plan
287   * @return the existing favored nodes plan
288   */
289  public FavoredNodesPlan getExistingAssignmentPlan() {
290    return this.existingAssignmentPlan;
291  }
292
293  /**
294   * Get the table set
295   * @return the table set
296   */
297  public Set<TableName> getTableSet() {
298    return this.tableToRegionMap.keySet();
299  }
300
301  public Map<ServerName, List<RegionInfo>> getSecondaryToRegionInfoMap() {
302    return this.secondaryRSToRegionMap;
303  }
304
305  public Map<ServerName, List<RegionInfo>> getTertiaryToRegionInfoMap() {
306    return this.teritiaryRSToRegionMap;
307  }
308
309  public Map<ServerName, List<RegionInfo>> getPrimaryToRegionInfoMap() {
310    return this.primaryRSToRegionMap;
311  }
312}