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