| /** |
| * Copyright 2009 The Apache Software Foundation |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.hadoop.hbase.client; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.hbase.HConstants; |
| import org.apache.hadoop.hbase.HRegionInfo; |
| import org.apache.hadoop.hbase.TableNotFoundException; |
| import org.apache.hadoop.hbase.util.Bytes; |
| import org.apache.hadoop.hbase.util.Writables; |
| |
| /** |
| * Scanner class that contains the <code>.META.</code> table scanning logic |
| * and uses a Retryable scanner. Provided visitors will be called |
| * for each row. |
| * |
| * Although public visibility, this is not a public-facing API and may evolve in |
| * minor releases. |
| */ |
| public class MetaScanner { |
| private static final Log LOG = LogFactory.getLog(MetaScanner.class); |
| /** |
| * Scans the meta table and calls a visitor on each RowResult and uses a empty |
| * start row value as table name. |
| * |
| * @param configuration conf |
| * @param visitor A custom visitor |
| * @throws IOException e |
| */ |
| public static void metaScan(Configuration configuration, |
| MetaScannerVisitor visitor) |
| throws IOException { |
| metaScan(configuration, visitor, null); |
| } |
| |
| /** |
| * Scans the meta table and calls a visitor on each RowResult. Uses a table |
| * name to locate meta regions. |
| * |
| * @param configuration config |
| * @param visitor visitor object |
| * @param userTableName User table name in meta table to start scan at. Pass |
| * null if not interested in a particular table. |
| * @throws IOException e |
| */ |
| public static void metaScan(Configuration configuration, |
| MetaScannerVisitor visitor, byte [] userTableName) |
| throws IOException { |
| metaScan(configuration, visitor, userTableName, null, Integer.MAX_VALUE); |
| } |
| |
| /** |
| * Scans the meta table and calls a visitor on each RowResult. Uses a table |
| * name and a row name to locate meta regions. And it only scans at most |
| * <code>rowLimit</code> of rows. |
| * |
| * @param configuration HBase configuration. |
| * @param visitor Visitor object. |
| * @param userTableName User table name in meta table to start scan at. Pass |
| * null if not interested in a particular table. |
| * @param row Name of the row at the user table. The scan will start from |
| * the region row where the row resides. |
| * @param rowLimit Max of processed rows. If it is less than 0, it |
| * will be set to default value <code>Integer.MAX_VALUE</code>. |
| * @throws IOException e |
| */ |
| public static void metaScan(Configuration configuration, |
| MetaScannerVisitor visitor, byte [] userTableName, byte[] row, |
| int rowLimit) |
| throws IOException { |
| metaScan(configuration, visitor, userTableName, row, rowLimit, |
| HConstants.META_TABLE_NAME); |
| } |
| |
| /** |
| * Scans the meta table and calls a visitor on each RowResult. Uses a table |
| * name and a row name to locate meta regions. And it only scans at most |
| * <code>rowLimit</code> of rows. |
| * |
| * @param configuration HBase configuration. |
| * @param visitor Visitor object. |
| * @param tableName User table name in meta table to start scan at. Pass |
| * null if not interested in a particular table. |
| * @param row Name of the row at the user table. The scan will start from |
| * the region row where the row resides. |
| * @param rowLimit Max of processed rows. If it is less than 0, it |
| * will be set to default value <code>Integer.MAX_VALUE</code>. |
| * @param metaTableName Meta table to scan, root or meta. |
| * @throws IOException e |
| */ |
| public static void metaScan(Configuration configuration, |
| MetaScannerVisitor visitor, byte [] tableName, byte[] row, |
| int rowLimit, final byte [] metaTableName) |
| throws IOException { |
| int rowUpperLimit = rowLimit > 0 ? rowLimit: Integer.MAX_VALUE; |
| |
| HConnection connection = HConnectionManager.getConnection(configuration); |
| // if row is not null, we want to use the startKey of the row's region as |
| // the startRow for the meta scan. |
| byte[] startRow; |
| if (row != null) { |
| // Scan starting at a particular row in a particular table |
| assert tableName != null; |
| byte[] searchRow = |
| HRegionInfo.createRegionName(tableName, row, HConstants.NINES, |
| false); |
| |
| HTable metaTable = new HTable(configuration, HConstants.META_TABLE_NAME); |
| Result startRowResult = metaTable.getRowOrBefore(searchRow, |
| HConstants.CATALOG_FAMILY); |
| if (startRowResult == null) { |
| throw new TableNotFoundException("Cannot find row in .META. for table: " |
| + Bytes.toString(tableName) + ", row=" + Bytes.toString(searchRow)); |
| } |
| byte[] value = startRowResult.getValue(HConstants.CATALOG_FAMILY, |
| HConstants.REGIONINFO_QUALIFIER); |
| if (value == null || value.length == 0) { |
| throw new IOException("HRegionInfo was null or empty in Meta for " + |
| Bytes.toString(tableName) + ", row=" + Bytes.toString(searchRow)); |
| } |
| HRegionInfo regionInfo = Writables.getHRegionInfo(value); |
| |
| byte[] rowBefore = regionInfo.getStartKey(); |
| startRow = HRegionInfo.createRegionName(tableName, rowBefore, |
| HConstants.ZEROES, false); |
| } else if (tableName == null || tableName.length == 0) { |
| // Full META scan |
| startRow = HConstants.EMPTY_START_ROW; |
| } else { |
| // Scan META for an entire table |
| startRow = HRegionInfo.createRegionName( |
| tableName, HConstants.EMPTY_START_ROW, HConstants.ZEROES, false); |
| } |
| |
| // Scan over each meta region |
| ScannerCallable callable; |
| int rows = Math.min(rowLimit, |
| configuration.getInt("hbase.meta.scanner.caching", 100)); |
| do { |
| final Scan scan = new Scan(startRow).addFamily(HConstants.CATALOG_FAMILY); |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("Scanning " + Bytes.toString(metaTableName) + |
| " starting at row=" + Bytes.toString(startRow) + " for max=" + |
| rowUpperLimit + " rows"); |
| } |
| callable = new ScannerCallable(connection, metaTableName, scan); |
| // Open scanner |
| connection.getRegionServerWithRetries(callable); |
| |
| int processedRows = 0; |
| try { |
| callable.setCaching(rows); |
| done: do { |
| if (processedRows >= rowUpperLimit) { |
| break; |
| } |
| //we have all the rows here |
| Result [] rrs = connection.getRegionServerWithRetries(callable); |
| if (rrs == null || rrs.length == 0 || rrs[0].size() == 0) { |
| break; //exit completely |
| } |
| for (Result rr : rrs) { |
| if (processedRows >= rowUpperLimit) { |
| break done; |
| } |
| if (!visitor.processRow(rr)) |
| break done; //exit completely |
| processedRows++; |
| } |
| //here, we didn't break anywhere. Check if we have more rows |
| } while(true); |
| // Advance the startRow to the end key of the current region |
| startRow = callable.getHRegionInfo().getEndKey(); |
| } finally { |
| // Close scanner |
| callable.setClose(); |
| connection.getRegionServerWithRetries(callable); |
| } |
| } while (Bytes.compareTo(startRow, HConstants.LAST_ROW) != 0); |
| } |
| |
| /** |
| * Lists all of the regions currently in META. |
| * @param conf |
| * @return List of all user-space regions. |
| * @throws IOException |
| */ |
| public static List<HRegionInfo> listAllRegions(Configuration conf) |
| throws IOException { |
| return listAllRegions(conf, true); |
| } |
| |
| /** |
| * Lists all of the regions currently in META. |
| * @param conf |
| * @param offlined True if we are to include offlined regions, false and we'll |
| * leave out offlined regions from returned list. |
| * @return List of all user-space regions. |
| * @throws IOException |
| */ |
| public static List<HRegionInfo> listAllRegions(Configuration conf, final boolean offlined) |
| throws IOException { |
| final List<HRegionInfo> regions = new ArrayList<HRegionInfo>(); |
| MetaScannerVisitor visitor = |
| new MetaScannerVisitor() { |
| @Override |
| public boolean processRow(Result result) throws IOException { |
| if (result == null || result.isEmpty()) { |
| return true; |
| } |
| byte [] bytes = result.getValue(HConstants.CATALOG_FAMILY, |
| HConstants.REGIONINFO_QUALIFIER); |
| if (bytes == null) { |
| LOG.warn("Null REGIONINFO_QUALIFIER: " + result); |
| return true; |
| } |
| HRegionInfo regionInfo = Writables.getHRegionInfo(bytes); |
| // If region offline AND we are not to include offlined regions, return. |
| if (regionInfo.isOffline() && !offlined) return true; |
| regions.add(regionInfo); |
| return true; |
| } |
| }; |
| metaScan(conf, visitor); |
| return regions; |
| } |
| |
| /** |
| * Visitor class called to process each row of the .META. table |
| */ |
| public interface MetaScannerVisitor { |
| /** |
| * Visitor method that accepts a RowResult and the meta region location. |
| * Implementations can return false to stop the region's loop if it becomes |
| * unnecessary for some reason. |
| * |
| * @param rowResult result |
| * @return A boolean to know if it should continue to loop in the region |
| * @throws IOException e |
| */ |
| public boolean processRow(Result rowResult) throws IOException; |
| } |
| } |