View Javadoc

1   /**
2    * Copyright 2009 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package org.apache.hadoop.hbase.client;
22  
23  import java.io.IOException;
24  import java.util.ArrayList;
25  import java.util.List;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.hadoop.conf.Configuration;
30  import org.apache.hadoop.hbase.HConstants;
31  import org.apache.hadoop.hbase.HRegionInfo;
32  import org.apache.hadoop.hbase.TableNotFoundException;
33  import org.apache.hadoop.hbase.util.Bytes;
34  import org.apache.hadoop.hbase.util.Writables;
35  
36  /**
37   * Scanner class that contains the <code>.META.</code> table scanning logic
38   * and uses a Retryable scanner. Provided visitors will be called
39   * for each row.
40   * 
41   * Although public visibility, this is not a public-facing API and may evolve in
42   * minor releases.
43   */
44  public class MetaScanner {
45    private static final Log LOG = LogFactory.getLog(MetaScanner.class);
46    /**
47     * Scans the meta table and calls a visitor on each RowResult and uses a empty
48     * start row value as table name.
49     *
50     * @param configuration conf
51     * @param visitor A custom visitor
52     * @throws IOException e
53     */
54    public static void metaScan(Configuration configuration,
55        MetaScannerVisitor visitor)
56    throws IOException {
57      metaScan(configuration, visitor, null);
58    }
59  
60    /**
61     * Scans the meta table and calls a visitor on each RowResult. Uses a table
62     * name to locate meta regions.
63     *
64     * @param configuration config
65     * @param visitor visitor object
66     * @param userTableName User table name in meta table to start scan at.  Pass
67     * null if not interested in a particular table.
68     * @throws IOException e
69     */
70    public static void metaScan(Configuration configuration,
71        MetaScannerVisitor visitor, byte [] userTableName)
72    throws IOException {
73      metaScan(configuration, visitor, userTableName, null, Integer.MAX_VALUE);
74    }
75  
76    /**
77     * Scans the meta table and calls a visitor on each RowResult. Uses a table
78     * name and a row name to locate meta regions. And it only scans at most
79     * <code>rowLimit</code> of rows.
80     *
81     * @param configuration HBase configuration.
82     * @param visitor Visitor object.
83     * @param userTableName User table name in meta table to start scan at.  Pass
84     * null if not interested in a particular table.
85     * @param row Name of the row at the user table. The scan will start from
86     * the region row where the row resides.
87     * @param rowLimit Max of processed rows. If it is less than 0, it
88     * will be set to default value <code>Integer.MAX_VALUE</code>.
89     * @throws IOException e
90     */
91    public static void metaScan(Configuration configuration,
92        MetaScannerVisitor visitor, byte [] userTableName, byte[] row,
93        int rowLimit)
94    throws IOException {
95      metaScan(configuration, visitor, userTableName, row, rowLimit,
96        HConstants.META_TABLE_NAME);
97    }
98  
99    /**
100    * Scans the meta table and calls a visitor on each RowResult. Uses a table
101    * name and a row name to locate meta regions. And it only scans at most
102    * <code>rowLimit</code> of rows.
103    *
104    * @param configuration HBase configuration.
105    * @param visitor Visitor object.
106    * @param tableName User table name in meta table to start scan at.  Pass
107    * null if not interested in a particular table.
108    * @param row Name of the row at the user table. The scan will start from
109    * the region row where the row resides.
110    * @param rowLimit Max of processed rows. If it is less than 0, it
111    * will be set to default value <code>Integer.MAX_VALUE</code>.
112    * @param metaTableName Meta table to scan, root or meta.
113    * @throws IOException e
114    */
115   public static void metaScan(Configuration configuration,
116       MetaScannerVisitor visitor, byte [] tableName, byte[] row,
117       int rowLimit, final byte [] metaTableName)
118   throws IOException {
119     int rowUpperLimit = rowLimit > 0 ? rowLimit: Integer.MAX_VALUE;
120 
121     HConnection connection = HConnectionManager.getConnection(configuration);
122     // if row is not null, we want to use the startKey of the row's region as
123     // the startRow for the meta scan.
124     byte[] startRow;
125     if (row != null) {
126       // Scan starting at a particular row in a particular table
127       assert tableName != null;
128       byte[] searchRow =
129         HRegionInfo.createRegionName(tableName, row, HConstants.NINES,
130           false);
131 
132       HTable metaTable = new HTable(configuration, HConstants.META_TABLE_NAME);
133       Result startRowResult = metaTable.getRowOrBefore(searchRow,
134           HConstants.CATALOG_FAMILY);
135       if (startRowResult == null) {
136         throw new TableNotFoundException("Cannot find row in .META. for table: "
137             + Bytes.toString(tableName) + ", row=" + Bytes.toStringBinary(searchRow));
138       }
139       byte[] value = startRowResult.getValue(HConstants.CATALOG_FAMILY,
140           HConstants.REGIONINFO_QUALIFIER);
141       if (value == null || value.length == 0) {
142         throw new IOException("HRegionInfo was null or empty in Meta for " +
143           Bytes.toString(tableName) + ", row=" + Bytes.toStringBinary(searchRow));
144       }
145       HRegionInfo regionInfo = Writables.getHRegionInfo(value);
146 
147       byte[] rowBefore = regionInfo.getStartKey();
148       startRow = HRegionInfo.createRegionName(tableName, rowBefore,
149           HConstants.ZEROES, false);
150     } else if (tableName == null || tableName.length == 0) {
151       // Full META scan
152       startRow = HConstants.EMPTY_START_ROW;
153     } else {
154       // Scan META for an entire table
155       startRow = HRegionInfo.createRegionName(
156           tableName, HConstants.EMPTY_START_ROW, HConstants.ZEROES, false);
157     }
158 
159     // Scan over each meta region
160     ScannerCallable callable;
161     int rows = Math.min(rowLimit,
162         configuration.getInt("hbase.meta.scanner.caching", 100));
163     do {
164       final Scan scan = new Scan(startRow).addFamily(HConstants.CATALOG_FAMILY);
165       if (LOG.isDebugEnabled()) {
166         LOG.debug("Scanning " + Bytes.toString(metaTableName) +
167           " starting at row=" + Bytes.toStringBinary(startRow) + " for max=" +
168           rowUpperLimit + " rows");
169       }
170       callable = new ScannerCallable(connection, metaTableName, scan);
171       // Open scanner
172       connection.getRegionServerWithRetries(callable);
173 
174       int processedRows = 0;
175       try {
176         callable.setCaching(rows);
177         done: do {
178           if (processedRows >= rowUpperLimit) {
179             break;
180           }
181           //we have all the rows here
182           Result [] rrs = connection.getRegionServerWithRetries(callable);
183           if (rrs == null || rrs.length == 0 || rrs[0].size() == 0) {
184             break; //exit completely
185           }
186           for (Result rr : rrs) {
187             if (processedRows >= rowUpperLimit) {
188               break done;
189             }
190             if (!visitor.processRow(rr))
191               break done; //exit completely
192             processedRows++;
193           }
194           //here, we didn't break anywhere. Check if we have more rows
195         } while(true);
196         // Advance the startRow to the end key of the current region
197         startRow = callable.getHRegionInfo().getEndKey();
198       } finally {
199         // Close scanner
200         callable.setClose();
201         connection.getRegionServerWithRetries(callable);
202       }
203     } while (Bytes.compareTo(startRow, HConstants.LAST_ROW) != 0);
204   }
205 
206   /**
207    * Lists all of the regions currently in META.
208    * @param conf
209    * @return List of all user-space regions.
210    * @throws IOException
211    */
212   public static List<HRegionInfo> listAllRegions(Configuration conf)
213   throws IOException {
214     return listAllRegions(conf, true);
215   }
216 
217   /**
218    * Lists all of the regions currently in META.
219    * @param conf
220    * @param offlined True if we are to include offlined regions, false and we'll
221    * leave out offlined regions from returned list.
222    * @return List of all user-space regions.
223    * @throws IOException
224    */
225   public static List<HRegionInfo> listAllRegions(Configuration conf, final boolean offlined)
226   throws IOException {
227     final List<HRegionInfo> regions = new ArrayList<HRegionInfo>();
228     MetaScannerVisitor visitor =
229       new MetaScannerVisitor() {
230         @Override
231         public boolean processRow(Result result) throws IOException {
232           if (result == null || result.isEmpty()) {
233             return true;
234           }
235           byte [] bytes = result.getValue(HConstants.CATALOG_FAMILY,
236             HConstants.REGIONINFO_QUALIFIER);
237           if (bytes == null) {
238             LOG.warn("Null REGIONINFO_QUALIFIER: " + result);
239             return true;
240           }
241           HRegionInfo regionInfo = Writables.getHRegionInfo(bytes);
242           // If region offline AND we are not to include offlined regions, return.
243           if (regionInfo.isOffline() && !offlined) return true;
244           regions.add(regionInfo);
245           return true;
246         }
247     };
248     metaScan(conf, visitor);
249     return regions;
250   }
251 
252   /**
253    * Visitor class called to process each row of the .META. table
254    */
255   public interface MetaScannerVisitor {
256     /**
257      * Visitor method that accepts a RowResult and the meta region location.
258      * Implementations can return false to stop the region's loop if it becomes
259      * unnecessary for some reason.
260      *
261      * @param rowResult result
262      * @return A boolean to know if it should continue to loop in the region
263      * @throws IOException e
264      */
265     public boolean processRow(Result rowResult) throws IOException;
266   }
267 }