View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase.client;
21  
22  import java.io.Closeable;
23  import java.io.IOException;
24  import java.util.ArrayList;
25  import java.util.List;
26  import java.util.NavigableMap;
27  import java.util.TreeMap;
28  
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  import org.apache.hadoop.hbase.classification.InterfaceAudience;
32  import org.apache.hadoop.conf.Configuration;
33  import org.apache.hadoop.hbase.HConstants;
34  import org.apache.hadoop.hbase.HRegionInfo;
35  import org.apache.hadoop.hbase.HRegionLocation;
36  import org.apache.hadoop.hbase.MetaTableAccessor;
37  import org.apache.hadoop.hbase.RegionLocations;
38  import org.apache.hadoop.hbase.ServerName;
39  import org.apache.hadoop.hbase.TableName;
40  import org.apache.hadoop.hbase.TableNotFoundException;
41  import org.apache.hadoop.hbase.util.Bytes;
42  import org.apache.hadoop.hbase.util.ExceptionUtil;
43  
44  /**
45   * Scanner class that contains the <code>hbase:meta</code> table scanning logic.
46   * Provided visitors will be called for each row.
47   *
48   * Although public visibility, this is not a public-facing API and may evolve in
49   * minor releases.
50   *
51   * <p> Note that during concurrent region splits, the scanner might not see
52   * hbase:meta changes across rows (for parent and daughter entries) consistently.
53   * see HBASE-5986, and {@link DefaultMetaScannerVisitor} for details. </p>
54   */
55  @InterfaceAudience.Private
56  //TODO: merge this to MetaTableAccessor, get rid of it.
57  public class MetaScanner {
58    private static final Log LOG = LogFactory.getLog(MetaScanner.class);
59    /**
60     * Scans the meta table and calls a visitor on each RowResult and uses a empty
61     * start row value as table name.
62     * 
63     * <p>Visible for testing. Use {@link
64     * #metaScan(Connection, MetaScannerVisitor, TableName)} instead.
65     *
66     * @param visitor A custom visitor
67     * @throws IOException e
68     */
69    // Do not use. Used by tests only and hbck.
70    public static void metaScan(Connection connection,
71        MetaScannerVisitor visitor) throws IOException {
72      metaScan(connection, visitor, null, null, Integer.MAX_VALUE);
73    }
74  
75    /**
76     * Scans the meta table and calls a visitor on each RowResult. Uses a table
77     * name to locate meta regions.
78     *
79     * @param connection connection to use internally (null to use a new instance)
80     * @param visitor visitor object
81     * @param userTableName User table name in meta table to start scan at.  Pass
82     * null if not interested in a particular table.
83     * @throws IOException e
84     */
85    public static void metaScan(Connection connection,
86        MetaScannerVisitor visitor, TableName userTableName) throws IOException {
87      metaScan(connection, visitor, userTableName, null, Integer.MAX_VALUE,
88          TableName.META_TABLE_NAME);
89    }
90  
91    /**
92     * Scans the meta table and calls a visitor on each RowResult. Uses a table
93     * name and a row name to locate meta regions. And it only scans at most
94     * <code>rowLimit</code> of rows.
95     * 
96     * <p>Visible for testing. Use {@link
97     * #metaScan(Connection, MetaScannerVisitor, TableName)} instead.
98     *
99     * @param connection to scan on
100    * @param visitor Visitor object.
101    * @param userTableName User table name in meta table to start scan at.  Pass
102    * null if not interested in a particular table.
103    * @param row Name of the row at the user table. The scan will start from
104    * the region row where the row resides.
105    * @param rowLimit Max of processed rows. If it is less than 0, it
106    * will be set to default value <code>Integer.MAX_VALUE</code>.
107    * @throws IOException e
108    */
109   // Do not use. Used by Master but by a method that is used testing.
110   public static void metaScan(Connection connection,
111       MetaScannerVisitor visitor, TableName userTableName, byte[] row,
112       int rowLimit)
113   throws IOException {
114     metaScan(connection, visitor, userTableName, row, rowLimit, TableName
115         .META_TABLE_NAME);
116   }
117 
118   /**
119    * Scans the meta table and calls a visitor on each RowResult. Uses a table
120    * name and a row name to locate meta regions. And it only scans at most
121    * <code>rowLimit</code> of rows.
122    *
123    * @param connection connection to use internally (null to use a new instance)
124    * @param visitor Visitor object. Closes the visitor before returning.
125    * @param tableName User table name in meta table to start scan at.  Pass
126    * null if not interested in a particular table.
127    * @param row Name of the row at the user table. The scan will start from
128    * the region row where the row resides.
129    * @param rowLimit Max of processed rows. If it is less than 0, it
130    * will be set to default value <code>Integer.MAX_VALUE</code>.
131    * @param metaTableName Meta table to scan, root or meta.
132    * @throws IOException e
133    */
134   static void metaScan(Connection connection,
135       final MetaScannerVisitor visitor, final TableName tableName,
136       final byte[] row, final int rowLimit, final TableName metaTableName)
137     throws IOException {
138 
139     int rowUpperLimit = rowLimit > 0 ? rowLimit: Integer.MAX_VALUE;
140     // Calculate startrow for scan.
141     byte[] startRow;
142     // If the passed in 'connection' is 'managed' -- i.e. every second test uses
143     // an HTable or an HBaseAdmin with managed connections -- then doing
144     // connection.getTable will throw an exception saying you are NOT to use
145     // managed connections getting tables.  Leaving this as it is for now. Will
146     // revisit when inclined to change all tests.  User code probaby makes use of
147     // managed connections too so don't change it till post hbase 1.0.
148     try (Table metaTable = new HTable(metaTableName, connection, null)) {
149       if (row != null) {
150         // Scan starting at a particular row in a particular table
151         Result startRowResult = getClosestRowOrBefore(metaTable, tableName, row,
152             connection.getConfiguration().getBoolean(HConstants.USE_META_REPLICAS,
153                 HConstants.DEFAULT_USE_META_REPLICAS));
154         if (startRowResult == null) {
155           throw new TableNotFoundException("Cannot find row in " + metaTable.getName() +
156             " for table: " + tableName + ", row=" + Bytes.toStringBinary(row));
157         }
158         HRegionInfo regionInfo = getHRegionInfo(startRowResult);
159         if (regionInfo == null) {
160           throw new IOException("HRegionInfo was null or empty in Meta for " +
161             tableName + ", row=" + Bytes.toStringBinary(row));
162         }
163         byte[] rowBefore = regionInfo.getStartKey();
164         startRow = HRegionInfo.createRegionName(tableName, rowBefore, HConstants.ZEROES, false);
165       } else if (tableName == null || tableName.getName().length == 0) {
166         // Full hbase:meta scan
167         startRow = HConstants.EMPTY_START_ROW;
168       } else {
169         // Scan hbase:meta for an entire table
170         startRow = HRegionInfo.createRegionName(tableName, HConstants.EMPTY_START_ROW,
171           HConstants.ZEROES, false);
172       }
173       final Scan scan = new Scan(startRow).addFamily(HConstants.CATALOG_FAMILY);
174       int scannerCaching = connection.getConfiguration()
175           .getInt(HConstants.HBASE_META_SCANNER_CACHING,
176               HConstants.DEFAULT_HBASE_META_SCANNER_CACHING);
177       if (connection.getConfiguration().getBoolean(HConstants.USE_META_REPLICAS,
178                 HConstants.DEFAULT_USE_META_REPLICAS)) {
179         scan.setConsistency(Consistency.TIMELINE);
180       }
181       if (rowUpperLimit <= scannerCaching) {
182           scan.setSmall(true);
183       }
184       int rows = Math.min(rowLimit, scannerCaching);
185       scan.setCaching(rows);
186       if (LOG.isTraceEnabled()) {
187         LOG.trace("Scanning " + metaTableName.getNameAsString() + " starting at row=" +
188           Bytes.toStringBinary(startRow) + " for max=" + rowUpperLimit + " with caching=" + rows);
189       }
190       // Run the scan
191       try (ResultScanner resultScanner = metaTable.getScanner(scan)) {
192         Result result;
193         int processedRows = 0;
194         while ((result = resultScanner.next()) != null) {
195           if (visitor != null) {
196             if (!visitor.processRow(result)) break;
197           }
198           processedRows++;
199           if (processedRows >= rowUpperLimit) break;
200         }
201       }
202     } finally {
203       if (visitor != null) {
204         try {
205           visitor.close();
206         } catch (Throwable t) {
207           ExceptionUtil.rethrowIfInterrupt(t);
208           LOG.debug("Got exception in closing the meta scanner visitor", t);
209         }
210       }
211     }
212   }
213 
214   /**
215    * @return Get closest metatable region row to passed <code>row</code>
216    * @throws IOException
217    */
218   private static Result getClosestRowOrBefore(final Table metaTable, final TableName userTableName,
219       final byte [] row, boolean useMetaReplicas)
220   throws IOException {
221     byte[] searchRow = HRegionInfo.createRegionName(userTableName, row, HConstants.NINES, false);
222     Scan scan = Scan.createGetClosestRowOrBeforeReverseScan(searchRow);
223     // Adding a filter on CATALOG_FAMILY is necessary for compatibility
224     // with hbase 2.x and beyond, which adds additional column families.
225     // See HBASE-26797
226     scan.addFamily(HConstants.CATALOG_FAMILY);
227     if (useMetaReplicas) {
228       scan.setConsistency(Consistency.TIMELINE);
229     }
230     try (ResultScanner resultScanner = metaTable.getScanner(scan)) {
231       return resultScanner.next();
232     }
233   }
234 
235   /**
236    * Returns HRegionInfo object from the column
237    * HConstants.CATALOG_FAMILY:HConstants.REGIONINFO_QUALIFIER of the catalog
238    * table Result.
239    * @param data a Result object from the catalog table scan
240    * @return HRegionInfo or null
241    * @deprecated Use {@link org.apache.hadoop.hbase.MetaTableAccessor#getRegionLocations(Result)}
242    */
243   @Deprecated
244   public static HRegionInfo getHRegionInfo(Result data) {
245     return HRegionInfo.getHRegionInfo(data);
246   }
247 
248   /**
249    * Lists all of the regions currently in META.
250    * @param conf configuration
251    * @param connection to connect with
252    * @param offlined True if we are to include offlined regions, false and we'll
253    * leave out offlined regions from returned list.
254    * @return List of all user-space regions.
255    * @throws IOException
256    */
257   // And for hbck.
258   public static List<HRegionInfo> listAllRegions(Configuration conf, Connection connection,
259       final boolean offlined)
260   throws IOException {
261     final List<HRegionInfo> regions = new ArrayList<HRegionInfo>();
262     MetaScannerVisitor visitor = new MetaScannerVisitorBase() {
263         @Override
264         public boolean processRow(Result result) throws IOException {
265           if (result == null || result.isEmpty()) {
266             return true;
267           }
268 
269           RegionLocations locations = MetaTableAccessor.getRegionLocations(result);
270           if (locations == null) return true;
271           for (HRegionLocation loc : locations.getRegionLocations()) {
272             if (loc != null) {
273               HRegionInfo regionInfo = loc.getRegionInfo();
274               // If region offline AND we are not to include offlined regions, return.
275               if (regionInfo.isOffline() && !offlined) continue;
276               regions.add(regionInfo);
277             }
278           }
279           return true;
280         }
281     };
282     metaScan(connection, visitor);
283     return regions;
284   }
285 
286   /**
287    * Lists all of the table regions currently in META.
288    * @param conf
289    * @param offlined True if we are to include offlined regions, false and we'll
290    * leave out offlined regions from returned list.
291    * @return Map of all user-space regions to servers
292    * @throws IOException
293    * @deprecated Use {@link #allTableRegions(Connection, TableName)} instead
294    */
295   @Deprecated
296   public static NavigableMap<HRegionInfo, ServerName> allTableRegions(Configuration conf,
297       Connection connection, final TableName tableName, boolean offlined) throws IOException {
298     return allTableRegions(connection, tableName);
299   }
300 
301   /**
302    * Lists all of the table regions currently in META.
303    * @param connection
304    * @param tableName
305    * @return Map of all user-space regions to servers
306    * @throws IOException
307    */
308   public static NavigableMap<HRegionInfo, ServerName> allTableRegions(
309       Connection connection, final TableName tableName) throws IOException {
310     final NavigableMap<HRegionInfo, ServerName> regions =
311       new TreeMap<HRegionInfo, ServerName>();
312     MetaScannerVisitor visitor = new TableMetaScannerVisitor(tableName) {
313       @Override
314       public boolean processRowInternal(Result result) throws IOException {
315         RegionLocations locations = MetaTableAccessor.getRegionLocations(result);
316         if (locations == null) return true;
317         for (HRegionLocation loc : locations.getRegionLocations()) {
318           if (loc != null) {
319             HRegionInfo regionInfo = loc.getRegionInfo();
320             regions.put(new UnmodifyableHRegionInfo(regionInfo), loc.getServerName());
321           }
322         }
323         return true;
324       }
325     };
326     metaScan(connection, visitor, tableName);
327     return regions;
328   }
329 
330   /**
331    * Lists table regions and locations grouped by region range from META.
332    */
333   public static List<RegionLocations> listTableRegionLocations(Configuration conf,
334       Connection connection, final TableName tableName) throws IOException {
335     final List<RegionLocations> regions = new ArrayList<RegionLocations>();
336     MetaScannerVisitor visitor = new TableMetaScannerVisitor(tableName) {
337       @Override
338       public boolean processRowInternal(Result result) throws IOException {
339         RegionLocations locations = MetaTableAccessor.getRegionLocations(result);
340         if (locations == null) return true;
341         regions.add(locations);
342         return true;
343       }
344     };
345     metaScan(connection, visitor, tableName);
346     return regions;
347   }
348 
349   /**
350    * Visitor class called to process each row of the hbase:meta table
351    */
352   public interface MetaScannerVisitor extends Closeable {
353     /**
354      * Visitor method that accepts a RowResult and the meta region location.
355      * Implementations can return false to stop the region's loop if it becomes
356      * unnecessary for some reason.
357      *
358      * @param rowResult result
359      * @return A boolean to know if it should continue to loop in the region
360      * @throws IOException e
361      */
362     boolean processRow(Result rowResult) throws IOException;
363   }
364 
365   public static abstract class MetaScannerVisitorBase implements MetaScannerVisitor {
366     @Override
367     public void close() throws IOException {
368     }
369   }
370 
371   /**
372    * A MetaScannerVisitor that skips offline regions and split parents
373    */
374   public static abstract class DefaultMetaScannerVisitor
375     extends MetaScannerVisitorBase {
376 
377     public DefaultMetaScannerVisitor() {
378       super();
379     }
380 
381     public abstract boolean processRowInternal(Result rowResult) throws IOException;
382 
383     @Override
384     public boolean processRow(Result rowResult) throws IOException {
385       HRegionInfo info = getHRegionInfo(rowResult);
386       if (info == null) {
387         return true;
388       }
389 
390       //skip over offline and split regions
391       if (!(info.isOffline() || info.isSplit())) {
392         return processRowInternal(rowResult);
393       }
394       return true;
395     }
396   }
397 
398   /**
399    * A MetaScannerVisitor for a table. Provides a consistent view of the table's
400    * hbase:meta entries during concurrent splits (see HBASE-5986 for details). This class
401    * does not guarantee ordered traversal of meta entries, and can block until the
402    * hbase:meta entries for daughters are available during splits.
403    */
404   public static abstract class TableMetaScannerVisitor extends DefaultMetaScannerVisitor {
405     private TableName tableName;
406 
407     public TableMetaScannerVisitor(TableName tableName) {
408       super();
409       this.tableName = tableName;
410     }
411 
412     @Override
413     public final boolean processRow(Result rowResult) throws IOException {
414       HRegionInfo info = getHRegionInfo(rowResult);
415       if (info == null) {
416         return true;
417       }
418       if (!(info.getTable().equals(tableName))) {
419         return false;
420       }
421       return super.processRow(rowResult);
422     }
423   }
424 }