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 }