Package com.alibaba.wasp.meta

Source Code of com.alibaba.wasp.meta.FMetaScanner$TableMetaScannerVisitor

/**
*
* 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 com.alibaba.wasp.meta;

import com.alibaba.wasp.EnityGroupOfflineException;
import com.alibaba.wasp.EntityGroupInfo;
import com.alibaba.wasp.FConstants;
import com.alibaba.wasp.MetaException;
import com.alibaba.wasp.ServerName;
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.client.Get;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.PrefixFilter;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.Pair;
import org.apache.hadoop.hbase.util.PairOfSameType;

import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

/**
* Scanner class that contains the <code>.FMETA.</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.
*
* <p>
* Note that during concurrent EG splits, the scanner might not see META changes
* across rows (for parent and daughter entries) consistently. see {@link BlockingMetaScannerVisitor} for details.
* </p>
*/
public class FMetaScanner extends AbstractMetaService {

  private static final Log LOG = LogFactory.getLog(FMetaScanner.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 java.io.IOException
   *           e
   */
  public static void metaScan(Configuration configuration,
      MetaScannerVisitor visitor) throws IOException {
    metaScan(configuration, visitor, null);
  }

  /**
   * Need to add comments.
   *
   * @param conf
   * @param entityGroupName
   * @return
   * @throws java.io.IOException
   */
  public static Pair<EntityGroupInfo, ServerName> getEntityGroup(
      Configuration conf, byte[] entityGroupName) throws IOException {
    return getService(conf).getEntityGroupAndLocation(entityGroupName);
  }

  /**
   * Scans the meta table and calls a visitor on each RowResult. Uses a table
   * name to locate meta entitGroups.
   *
   * @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 java.io.IOException
   *           e
   */
  public static void metaScan(Configuration configuration,
      MetaScannerVisitor visitor, byte[] userTableName) throws IOException {
    metaScan(configuration, visitor, userTableName, null, Integer.MAX_VALUE);
  }

  public static void metaScan(Configuration configuration,
      MetaScannerVisitor visitor, byte[] tableName, byte[] row, int rowLimit)
      throws IOException {
    int rowUpperLimit = rowLimit > 0 ? rowLimit : Integer.MAX_VALUE;

    // if row is not null, we want to use the startKey of the row's entityGroup
    // as
    // the startRow for the meta scan.
    byte[] startRow = tableName;
    if (tableName == null || tableName.length == 0) {
      // Full META scan
      startRow = FConstants.EMPTY_START_ROW;
    }
    String metaTableString = configuration.get(FConstants.METASTORE_TABLE,
        FConstants.DEFAULT_METASTORE_TABLE);
    HTable fmetaTable = new HTable(configuration, metaTableString);
    // Scan over each meta entityGroup
    int rows = Math.min(rowLimit, configuration.getInt(
        HConstants.HBASE_META_SCANNER_CACHING,
        HConstants.DEFAULT_HBASE_META_SCANNER_CACHING));

    final Scan scan = new Scan(startRow);
    if (LOG.isDebugEnabled()) {
      LOG.debug("Scanning " + metaTableString + " starting at row="
          + Bytes.toStringBinary(startRow) + " for max=" + rowUpperLimit
          + " rows ");
    }
    scan.addFamily(FConstants.CATALOG_FAMILY);
    scan.setCaching(rows);
    if (tableName == null || tableName.length == 0) {
      FilterList allFilters = new FilterList();
      allFilters.addFilter(new PrefixFilter(tableName));
      scan.setFilter(allFilters);
    }
    int processedRows = 0;
    try {
      ResultScanner scanner = fmetaTable.getScanner(scan);
      for (Result r = scanner.next(); r != null; r = scanner.next()) {
        if (processedRows >= rowUpperLimit) {
          break;
        }
        if (Bytes.startsWith(r.getRow(), FConstants.TABLEROW_PREFIX)) {
          continue;
        }
        if (!visitor.processRow(r))
          break; // exit completely
        processedRows++;
      }
      // here, we didn't break anywhere. Check if we have more rows
    } finally {
      // Close scanner
      fmetaTable.close();
    }
  }

  /**
   * Returns EntityGroupInfo object from the column
   * FConstants.CATALOG_FAMILY:FConstants.EGINFO of the catalog table Result.
   *
   * @param data a Result object from the catalog table scan
   * @return EntityGroupInfo or null
   */
  public static EntityGroupInfo getEntityGroupInfo(Result data) {
    byte[] bytes = data.getValue(FConstants.CATALOG_FAMILY, FConstants.EGINFO);
    if (bytes == null)
      return null;
    EntityGroupInfo info = EntityGroupInfo.parseFromOrNull(bytes);
    if (LOG.isDebugEnabled()) {
      LOG.debug("Current INFO from scan results = " + info);
    }
    return info;
  }

  /**
   * Lists all of the EntityGroups currently in META.
   *
   * @param conf
   * @return List of all user-space EntityGroups.
   * @throws java.io.IOException
   */
  public static List<EntityGroupInfo> listAllEntityGroups(Configuration conf)
      throws IOException {
    return listAllEntityGroups(conf, true);
  }

  /**
   * Lists all of the EntityGroups currently in META.
   *
   * @param conf
   * @param offlined
   *          True if we are to include offlined EntityGroups, false and we'll
   *          leave out offlined EntityGroups from returned list.
   * @return List of all user-space EntityGroups.
   * @throws java.io.IOException
   */
  public static List<EntityGroupInfo> listAllEntityGroups(Configuration conf,
      final boolean offlined) throws IOException {
    final List<EntityGroupInfo> entityGroups = new ArrayList<EntityGroupInfo>();
    MetaScannerVisitor visitor = new BlockingMetaScannerVisitor(conf) {
      @Override
      public boolean processRowInternal(Result result) throws IOException {
        if (result == null || result.isEmpty()) {
          return true;
        }

        EntityGroupInfo entityGroupInfo = getEntityGroupInfo(result);
        if (entityGroupInfo == null) {
          LOG.warn("Null ENTITYGROUPINFO_QUALIFIER: " + result);
          return true;
        }

        // If entityGroup offline AND we are not to include offlined entityGroups,
        // return.
        if (entityGroupInfo.isOffline() && !offlined)
          return true;
        entityGroups.add(entityGroupInfo);
        return true;
      }
    };
    metaScan(conf, visitor);
    return entityGroups;
  }

  /**
   * Get root table name
   *
   * @param FTable
   * @return
   */
  public static String getRootTable(FTable table) {
    if (table.isRootTable()) {
      return table.getTableName();
    } else {
      return table.getParentName();
    }
  }

  /**
   * Lists all of the table EntityGroups currently in META.
   *
   * @param conf
   * @param offlined
   *          True if we are to include offlined EntityGroups, false and we'll
   *          leave out offlined EntityGroups from returned list.
   * @return Map of all user-space EntityGroups to servers
   * @throws java.io.IOException
   */
  public static NavigableMap<EntityGroupInfo, ServerName> allTableEntityGroups(
      Configuration conf, final byte[] tablename, final boolean offlined)
      throws IOException {
    FTable table = getService(conf).getTable(Bytes.toString(tablename));
    final byte[] parentTableName = Bytes.toBytes(getRootTable(table));
    final NavigableMap<EntityGroupInfo, ServerName> entityGroups = new TreeMap<EntityGroupInfo, ServerName>();
    MetaScannerVisitor visitor = new TableMetaScannerVisitor(conf,
        parentTableName) {
      @Override
      public boolean processRowInternal(Result rowResult) throws IOException {
        EntityGroupInfo entityGroupInfo = getEntityGroupInfo(rowResult);
        ServerName sn = ServerName.getServerName(rowResult);

        if (!(entityGroupInfo.isOffline() || entityGroupInfo.isSplit())) {
          entityGroups.put(entityGroupInfo, sn);
        }
        return true;
      }
    };
    metaScan(conf, visitor, parentTableName);
    return entityGroups;
  }

  /**
   * Visitor class called to process each row of the .META. table
   */
  public interface MetaScannerVisitor extends Closeable {
    /**
     * Visitor method that accepts a RowResult and the meta entityGroup location.
     * Implementations can return false to stop the entityGroup'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
     *         entityGroup
     * @throws java.io.IOException
     *           e
     */
    public boolean processRow(Result rowResult) throws IOException;
  }

  public static abstract class MetaScannerVisitorBase implements
      MetaScannerVisitor {
    @Override
    public void close() throws IOException {
    }
  }

  /**
   * A MetaScannerVisitor that provides a consistent view of the table's META
   * entries during concurrent splits (see HBASE-5986 for details). This class
   * does not guarantee ordered traversal of meta entries, and can block until
   * the META entries for daughters are available during splits.
   */
  public static abstract class BlockingMetaScannerVisitor extends
      MetaScannerVisitorBase {

    private static final int DEFAULT_BLOCKING_TIMEOUT = 10000;
    private Configuration conf;
    private TreeSet<byte[]> daughterEntityGroups = new TreeSet<byte[]>(
        Bytes.BYTES_COMPARATOR);
    private int blockingTimeout;
    private HTable fmetaTable;

    public BlockingMetaScannerVisitor(Configuration conf) {
      this.conf = conf;
      this.blockingTimeout = conf.getInt(
          HConstants.HBASE_CLIENT_OPERATION_TIMEOUT, DEFAULT_BLOCKING_TIMEOUT);
    }

    public abstract boolean processRowInternal(Result rowResult)
        throws IOException;

    @Override
    public void close() throws IOException {
      super.close();
      if (fmetaTable != null) {
        fmetaTable.close();
        fmetaTable = null;
      }
    }

    public HTable getFMetaTable() throws IOException {
      if (fmetaTable == null) {
        String metaTableString = conf.get(FConstants.METASTORE_TABLE,
            FConstants.DEFAULT_METASTORE_TABLE);
        fmetaTable = new HTable(conf, metaTableString);
      }
      return fmetaTable;
    }

    @Override
    public boolean processRow(Result rowResult) throws IOException {
      EntityGroupInfo info = getEntityGroupInfo(rowResult);
      if (info == null) {
        return true;
      }

      if (daughterEntityGroups.remove(info.getEntityGroupName())) {
        return true; // we have already processed this row
      }

      if (info.isSplitParent()) {
        /*
         * we have found a parent entityGroup which was split. We have to ensure
         * that it's daughters are seen by this scanner as well, so we block
         * until they are added to the META table. Even though we are waiting
         * for META entries, ACID semantics in HBase indicates that this scanner
         * might not see the new rows. So we manually query the daughter rows
         */
        PairOfSameType<EntityGroupInfo> daughters = EntityGroupInfo
            .getDaughterEntityGroups(rowResult);
        EntityGroupInfo splitA = daughters.getFirst();
        EntityGroupInfo splitB = daughters.getSecond();

        HTable fmetaTable = getFMetaTable();
        long start = System.currentTimeMillis();
        Result resultA = getEntityGroupResultBlocking(fmetaTable,
            blockingTimeout, splitA.getEntityGroupName());
        if (resultA != null) {
          processRow(resultA);
          daughterEntityGroups.add(splitA.getEntityGroupName());
        } else {
          throw new EnityGroupOfflineException("Split daughter entityGroup "
              + splitA.getEntityGroupNameAsString()
              + " cannot be found in META.");
        }
        long rem = blockingTimeout - (System.currentTimeMillis() - start);

        Result resultB = getEntityGroupResultBlocking(fmetaTable, rem,
            splitB.getEntityGroupName());
        if (resultB != null) {
          processRow(resultB);
          daughterEntityGroups.add(splitB.getEntityGroupName());
        } else {
          throw new EnityGroupOfflineException("Split daughter entityGroup "
              + splitB.getEntityGroupNameAsString()
              + " cannot be found in META.");
        }
      }

      return processRowInternal(rowResult);
    }

    private Result getEntityGroupResultBlocking(HTable fmetaTable, long timeout,
        byte[] entityGroupName) throws IOException {
      if (LOG.isDebugEnabled()) {
        LOG.debug("blocking until entityGroup is in META: "
            + Bytes.toStringBinary(entityGroupName));
      }
      long start = System.currentTimeMillis();
      while (System.currentTimeMillis() - start < timeout) {
        Get get = new Get(entityGroupName);
        Result result = fmetaTable.get(get);
        EntityGroupInfo entityGroupInfo = getEntityGroupInfo(result);
        if (entityGroupInfo != null) {
          return result;
        }
        try {
          Thread.sleep(10);
        } catch (InterruptedException ex) {
          Thread.currentThread().interrupt();
          break;
        }
      }
      return null;
    }
  }

  /**
   * A MetaScannerVisitor for a table. Provides a consistent view of the table's
   * META entries during concurrent splits (see HBASE-5986 for details). This
   * class does not guarantee ordered traversal of meta entries, and can block
   * until the META entries for daughters are available during splits.
   */
  public static abstract class TableMetaScannerVisitor extends
      BlockingMetaScannerVisitor {
    private byte[] tableName;

    public TableMetaScannerVisitor(Configuration conf, byte[] tableName) {
      super(conf);
      this.tableName = tableName;
    }

    @Override
    public final boolean processRow(Result rowResult) throws IOException {
      EntityGroupInfo entityGroupInfo = getEntityGroupInfo(rowResult);
      if (entityGroupInfo == null) {
        return true;
      }
      if (!(Bytes.equals(entityGroupInfo.getTableName(), tableName))) {
        return false;
      }
      return super.processRow(rowResult);
    }

  }

  /**
   *
   * @param conf
   * @param disabledOrDisablingOrEnabling
   * @param excludeOfflinedSplitParents
   * @return
   * @throws com.alibaba.wasp.MetaException
   */
  public static Map<EntityGroupInfo, ServerName> fullScan(Configuration conf,
      Set<String> disabledOrDisablingOrEnabling,
      boolean excludeOfflinedSplitParents) throws MetaException {
    return getService(conf).fullScan(disabledOrDisablingOrEnabling,
        excludeOfflinedSplitParents);
  }

  /**
   *
   * @param conf
   * @return
   * @throws com.alibaba.wasp.MetaException
   */
  public static List<Result> fullScan(Configuration conf) throws MetaException {
    return getService(conf).fullScan();
  }

  /**
   *
   * @param conf
   * @param visitor
   * @param startrow
   * @param endrow
   * @throws com.alibaba.wasp.MetaException
   */
  public static void fullScan(final Configuration conf,
      final FMetaVisitor visitor, final byte[] startrow, final byte[] endrow)
      throws MetaException {
    getService(conf).fullScan(visitor, startrow, endrow);
  }
}
TOP

Related Classes of com.alibaba.wasp.meta.FMetaScanner$TableMetaScannerVisitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.