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 " +
107      "snappshot");
108    // TODO: at some point this code could live in the MetaTableAccessor
109    Visitor v = new Visitor() {
110      @Override
111      public boolean visit(Result result) throws IOException {
112        try {
113          if (result ==  null || result.isEmpty()) return true;
114          RegionLocations rl = MetaTableAccessor.getRegionLocations(result);
115          if (rl == null) return true;
116          RegionInfo hri = rl.getRegionLocation(0).getRegionInfo();
117          if (hri == null) return true;
118          if (hri.getTable() == null) return true;
119          if (disabledTables.contains(hri.getTable())) {
120            return true;
121          }
122          // Are we to include split parents in the list?
123          if (excludeOfflinedSplitParents && hri.isSplit()) return true;
124          HRegionLocation[] hrls = rl.getRegionLocations();
125
126          // Add the current assignment to the snapshot for all replicas
127          for (int i = 0; i < hrls.length; i++) {
128            if (hrls[i] == null) continue;
129            hri = hrls[i].getRegionInfo();
130            if (hri == null) continue;
131            addAssignment(hri, hrls[i].getServerName());
132            addRegion(hri);
133          }
134
135          hri = rl.getRegionLocation(0).getRegionInfo();
136          // the code below is to handle favored nodes
137          byte[] favoredNodes = result.getValue(HConstants.CATALOG_FAMILY,
138              FavoredNodeAssignmentHelper.FAVOREDNODES_QUALIFIER);
139          if (favoredNodes == null) return true;
140          // Add the favored nodes into assignment plan
141          ServerName[] favoredServerList =
142              FavoredNodeAssignmentHelper.getFavoredNodesList(favoredNodes);
143          // Add the favored nodes into assignment plan
144          existingAssignmentPlan.updateFavoredNodesMap(hri,
145              Arrays.asList(favoredServerList));
146
147          /*
148           * Typically there should be FAVORED_NODES_NUM favored nodes for a region in meta. If
149           * there is less than FAVORED_NODES_NUM, lets use as much as we can but log a warning.
150           */
151          if (favoredServerList.length != FavoredNodeAssignmentHelper.FAVORED_NODES_NUM) {
152            LOG.warn("Insufficient favored nodes for region " + hri + " fn: " + Arrays
153                .toString(favoredServerList));
154          }
155          for (int i = 0; i < favoredServerList.length; i++) {
156            if (i == PRIMARY.ordinal()) addPrimaryAssignment(hri, favoredServerList[i]);
157            if (i == SECONDARY.ordinal()) addSecondaryAssignment(hri, favoredServerList[i]);
158            if (i == TERTIARY.ordinal()) addTeritiaryAssignment(hri, favoredServerList[i]);
159          }
160          return true;
161        } catch (RuntimeException e) {
162          LOG.error("Catche remote exception " + e.getMessage() +
163              " when processing" + result);
164          throw e;
165        }
166      }
167    };
168    // Scan hbase:meta to pick up user regions
169    MetaTableAccessor.fullScanRegions(connection, v);
170    //regionToRegionServerMap = regions;
171    LOG.info("Finished to scan the hbase:meta for the current region assignment" +
172      "snapshot");
173  }
174
175  private void addRegion(RegionInfo regionInfo) {
176    // Process the region name to region info map
177    regionNameToRegionInfoMap.put(regionInfo.getRegionNameAsString(), regionInfo);
178
179    // Process the table to region map
180    TableName tableName = regionInfo.getTable();
181    List<RegionInfo> regionList = tableToRegionMap.get(tableName);
182    if (regionList == null) {
183      regionList = new ArrayList<>();
184    }
185    // Add the current region info into the tableToRegionMap
186    regionList.add(regionInfo);
187    tableToRegionMap.put(tableName, regionList);
188  }
189
190  private void addAssignment(RegionInfo regionInfo, ServerName server) {
191    // Process the region to region server map
192    regionToRegionServerMap.put(regionInfo, server);
193
194    if (server == null) return;
195
196    // Process the region server to region map
197    List<RegionInfo> regionList = currentRSToRegionMap.get(server);
198    if (regionList == null) {
199      regionList = new ArrayList<>();
200    }
201    regionList.add(regionInfo);
202    currentRSToRegionMap.put(server, regionList);
203  }
204
205  private void addPrimaryAssignment(RegionInfo regionInfo, ServerName server) {
206    // Process the region server to region map
207    List<RegionInfo> regionList = primaryRSToRegionMap.get(server);
208    if (regionList == null) {
209      regionList = new ArrayList<>();
210    }
211    regionList.add(regionInfo);
212    primaryRSToRegionMap.put(server, regionList);
213  }
214
215  private void addSecondaryAssignment(RegionInfo regionInfo, ServerName server) {
216    // Process the region server to region map
217    List<RegionInfo> regionList = secondaryRSToRegionMap.get(server);
218    if (regionList == null) {
219      regionList = new ArrayList<>();
220    }
221    regionList.add(regionInfo);
222    secondaryRSToRegionMap.put(server, regionList);
223  }
224
225  private void addTeritiaryAssignment(RegionInfo regionInfo, ServerName server) {
226    // Process the region server to region map
227    List<RegionInfo> regionList = teritiaryRSToRegionMap.get(server);
228    if (regionList == null) {
229      regionList = new ArrayList<>();
230    }
231    regionList.add(regionInfo);
232    teritiaryRSToRegionMap.put(server, regionList);
233  }
234
235  /**
236   * Get the regioninfo for a region
237   * @return the regioninfo
238   */
239  public Map<String, RegionInfo> getRegionNameToRegionInfoMap() {
240    return this.regionNameToRegionInfoMap;
241  }
242
243  /**
244   * Get regions for tables
245   * @return a mapping from table to regions
246   */
247  public Map<TableName, List<RegionInfo>> getTableToRegionMap() {
248    return tableToRegionMap;
249  }
250
251  /**
252   * Get region to region server map
253   * @return region to region server map
254   */
255  public Map<RegionInfo, ServerName> getRegionToRegionServerMap() {
256    return regionToRegionServerMap;
257  }
258
259  /**
260   * Get regionserver to region map
261   * @return regionserver to region map
262   */
263  public Map<ServerName, List<RegionInfo>> getRegionServerToRegionMap() {
264    return currentRSToRegionMap;
265  }
266
267  /**
268   * Get the favored nodes plan
269   * @return the existing favored nodes plan
270   */
271  public FavoredNodesPlan getExistingAssignmentPlan() {
272    return this.existingAssignmentPlan;
273  }
274
275  /**
276   * Get the table set
277   * @return the table set
278   */
279  public Set<TableName> getTableSet() {
280    return this.tableToRegionMap.keySet();
281  }
282
283  public Map<ServerName, List<RegionInfo>> getSecondaryToRegionInfoMap() {
284    return this.secondaryRSToRegionMap;
285  }
286
287  public Map<ServerName, List<RegionInfo>> getTertiaryToRegionInfoMap() {
288    return this.teritiaryRSToRegionMap;
289  }
290
291  public Map<ServerName, List<RegionInfo>> getPrimaryToRegionInfoMap() {
292    return this.primaryRSToRegionMap;
293  }
294}