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 org.apache.hadoop.conf.Configuration;
24  import org.apache.hadoop.hbase.HConstants;
25  import org.apache.hadoop.hbase.HRegionInfo;
26  import org.apache.hadoop.hbase.TableNotFoundException;
27  import org.apache.hadoop.hbase.util.Bytes;
28  import org.apache.hadoop.hbase.util.Writables;
29  
30  import java.io.IOException;
31  
32  /**
33   * Scanner class that contains the <code>.META.</code> table scanning logic
34   * and uses a Retryable scanner. Provided visitors will be called
35   * for each row.
36   * 
37   * Although public visibility, this is not a public-facing API and may evolve in
38   * minor releases.
39   */
40  public class MetaScanner {
41  
42    /**
43     * Scans the meta table and calls a visitor on each RowResult and uses a empty
44     * start row value as table name.
45     *
46     * @param configuration conf
47     * @param visitor A custom visitor
48     * @throws IOException e
49     */
50    public static void metaScan(Configuration configuration,
51        MetaScannerVisitor visitor)
52    throws IOException {
53      metaScan(configuration, visitor, HConstants.EMPTY_START_ROW);
54    }
55  
56    /**
57     * Scans the meta table and calls a visitor on each RowResult. Uses a table
58     * name to locate meta regions.
59     *
60     * @param configuration config
61     * @param visitor visitor object
62     * @param tableName table name
63     * @throws IOException e
64     */
65    public static void metaScan(Configuration configuration,
66        MetaScannerVisitor visitor, byte[] tableName)
67    throws IOException {
68      metaScan(configuration, visitor, tableName, null, Integer.MAX_VALUE);
69    }
70  
71    /**
72     * Scans the meta table and calls a visitor on each RowResult. Uses a table
73     * name and a row name to locate meta regions. And it only scans at most
74     * <code>rowLimit</code> of rows.
75     *
76     * @param configuration HBase configuration.
77     * @param visitor Visitor object.
78     * @param tableName User table name.
79     * @param row Name of the row at the user table. The scan will start from
80     * the region row where the row resides.
81     * @param rowLimit Max of processed rows. If it is less than 0, it
82     * will be set to default value <code>Integer.MAX_VALUE</code>.
83     * @throws IOException e
84     */
85    public static void metaScan(Configuration configuration,
86        MetaScannerVisitor visitor, byte[] tableName, byte[] row,
87        int rowLimit)
88    throws IOException {
89      int rowUpperLimit = rowLimit > 0 ? rowLimit: Integer.MAX_VALUE;
90  
91      HConnection connection = HConnectionManager.getConnection(configuration);
92      // if row is not null, we want to use the startKey of the row's region as
93      // the startRow for the meta scan.
94      byte[] startRow;
95      if (row != null) {
96        // Scan starting at a particular row in a particular table
97        assert tableName != null;
98        byte[] searchRow =
99          HRegionInfo.createRegionName(tableName, row, HConstants.NINES,
100           false);
101 
102       HTable metaTable = new HTable(configuration, HConstants.META_TABLE_NAME);
103       Result startRowResult = metaTable.getRowOrBefore(searchRow,
104           HConstants.CATALOG_FAMILY);
105       if (startRowResult == null) {
106         throw new TableNotFoundException("Cannot find row in .META. for table: "
107             + Bytes.toString(tableName) + ", row=" + Bytes.toString(searchRow));
108       }
109       byte[] value = startRowResult.getValue(HConstants.CATALOG_FAMILY,
110           HConstants.REGIONINFO_QUALIFIER);
111       if (value == null || value.length == 0) {
112         throw new IOException("HRegionInfo was null or empty in Meta for " +
113           Bytes.toString(tableName) + ", row=" + Bytes.toString(searchRow));
114       }
115       HRegionInfo regionInfo = Writables.getHRegionInfo(value);
116 
117       byte[] rowBefore = regionInfo.getStartKey();
118       startRow = HRegionInfo.createRegionName(tableName, rowBefore,
119           HConstants.ZEROES, false);
120     } else if (tableName == null || tableName.length == 0) {
121       // Full META scan
122       startRow = HConstants.EMPTY_START_ROW;
123     } else {
124       // Scan META for an entire table
125       startRow = HRegionInfo.createRegionName(
126           tableName, HConstants.EMPTY_START_ROW, HConstants.ZEROES, false);
127     }
128 
129     // Scan over each meta region
130     ScannerCallable callable;
131     int rows = Math.min(rowLimit,
132         configuration.getInt("hbase.meta.scanner.caching", 100));
133     do {
134       final Scan scan = new Scan(startRow).addFamily(HConstants.CATALOG_FAMILY);
135       callable = new ScannerCallable(connection, HConstants.META_TABLE_NAME,
136           scan);
137       // Open scanner
138       connection.getRegionServerWithRetries(callable);
139 
140       int processedRows = 0;
141       try {
142         callable.setCaching(rows);
143         done: do {
144           if (processedRows >= rowUpperLimit) {
145             break;
146           }
147           //we have all the rows here
148           Result [] rrs = connection.getRegionServerWithRetries(callable);
149           if (rrs == null || rrs.length == 0 || rrs[0].size() == 0) {
150             break; //exit completely
151           }
152           for (Result rr : rrs) {
153             if (processedRows >= rowUpperLimit) {
154               break done;
155             }
156             if (!visitor.processRow(rr))
157               break done; //exit completely
158             processedRows++;
159           }
160           //here, we didn't break anywhere. Check if we have more rows
161         } while(true);
162         // Advance the startRow to the end key of the current region
163         startRow = callable.getHRegionInfo().getEndKey();
164       } finally {
165         // Close scanner
166         callable.setClose();
167         connection.getRegionServerWithRetries(callable);
168       }
169     } while (Bytes.compareTo(startRow, HConstants.LAST_ROW) != 0);
170   }
171 
172   /**
173    * Visitor class called to process each row of the .META. table
174    */
175   public interface MetaScannerVisitor {
176     /**
177      * Visitor method that accepts a RowResult and the meta region location.
178      * Implementations can return false to stop the region's loop if it becomes
179      * unnecessary for some reason.
180      *
181      * @param rowResult result
182      * @return A boolean to know if it should continue to loop in the region
183      * @throws IOException e
184      */
185     public boolean processRow(Result rowResult) throws IOException;
186   }
187 }