Package org.apache.hadoop.hbase.index.coprocessor.regionserver

Source Code of org.apache.hadoop.hbase.index.coprocessor.regionserver.IndexRegionObserver

/**
* Copyright 2011 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.index.coprocessor.regionserver;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

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.HConstants.OperationStatusCode;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.Mutation;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
import org.apache.hadoop.hbase.coprocessor.RegionObserverExt;
import org.apache.hadoop.hbase.index.ColumnQualifier;
import org.apache.hadoop.hbase.index.Constants;
import org.apache.hadoop.hbase.index.IndexSpecification;
import org.apache.hadoop.hbase.index.IndexedHTableDescriptor;
import org.apache.hadoop.hbase.index.manager.IndexManager;
import org.apache.hadoop.hbase.index.util.ByteArrayBuilder;
import org.apache.hadoop.hbase.index.util.IndexUtils;
import org.apache.hadoop.hbase.regionserver.HRegion;
import org.apache.hadoop.hbase.regionserver.HRegionServer;
import org.apache.hadoop.hbase.regionserver.InternalScanner;
import org.apache.hadoop.hbase.regionserver.KeyValueScanner;
import org.apache.hadoop.hbase.regionserver.OperationStatus;
import org.apache.hadoop.hbase.regionserver.RegionScanner;
import org.apache.hadoop.hbase.regionserver.RegionServerServices;
import org.apache.hadoop.hbase.regionserver.ScanType;
import org.apache.hadoop.hbase.regionserver.SplitTransaction;
import org.apache.hadoop.hbase.regionserver.SplitTransaction.SplitInfo;
import org.apache.hadoop.hbase.regionserver.Store;
import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.Pair;
import org.apache.hadoop.hbase.util.PairOfSameType;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;

public class IndexRegionObserver extends BaseRegionObserver implements RegionObserverExt {

  private static final Log LOG = LogFactory.getLog(IndexRegionObserver.class);

  // variable will be set to true in test case for testing
  // All below public static fields are used for testing.
  static boolean isTestingEnabled = false;

  public static boolean isSeekpointAddded = false;

  public static boolean isIndexedFlowUsed = false;

  public static List<byte[]> seekPoints = null;

  public static List<byte[]> seekPointsForMultipleIndices = null;

  private Map<RegionScanner, SeekPointFetcher> scannerMap =
      new ConcurrentHashMap<RegionScanner, SeekPointFetcher>();

  private IndexManager indexManager = IndexManager.getInstance();

  public static final ThreadLocal<IndexEdits> threadLocal = new ThreadLocal<IndexEdits>() {
    @Override
    protected IndexEdits initialValue() {
      return new IndexEdits();
    }
  };

  @Override
  public void postOpen(ObserverContext<RegionCoprocessorEnvironment> contx) {
    byte[] tableName = contx.getEnvironment().getRegion().getTableDesc().getName();
    String tableNameStr = Bytes.toString(tableName);
    if (IndexUtils.isCatalogTable(tableName) || IndexUtils.isIndexTable(tableNameStr)) {
      return;
    }
    LOG.trace("Entering postOpen for the table " + tableNameStr);
    this.indexManager.incrementRegionCount(tableNameStr);
    List<IndexSpecification> list = indexManager.getIndicesForTable(tableNameStr);
    if (null != list) {
      LOG.trace("Index Manager already contains an entry for the table "
          + ". Hence returning from postOpen");
      return;
    }
    RegionServerServices rss = contx.getEnvironment().getRegionServerServices();
    Configuration conf = rss.getConfiguration();
    IndexedHTableDescriptor tableDescriptor = null;
    try {
      tableDescriptor = IndexUtils.getIndexedHTableDescriptor(tableName, conf);
    } catch (IOException e) {
      rss.abort("Some unidentified scenario while reading from the "
          + "table descriptor . Aborting RegionServer", e);
    }
    if (tableDescriptor != null) {
      list = tableDescriptor.getIndices();
      if (list != null && list.size() > 0) {
        indexManager.addIndexForTable(tableNameStr, list);
        LOG.trace("Added index Specification in the Manager for the " + tableNameStr);
      } else {
        list = new ArrayList<IndexSpecification>();
        indexManager.addIndexForTable(tableNameStr, list);
        LOG.trace("Added index Specification in the Manager for the " + tableNameStr);
      }
    }
    LOG.trace("Exiting postOpen for the table " + tableNameStr);
  }

  @Override
  public void preBatchMutate(final ObserverContext<RegionCoprocessorEnvironment> ctx,
      final List<Pair<Mutation, OperationStatus>> mutationVsBatchOp, final WALEdit edit)
      throws IOException {
    HRegionServer rs = (HRegionServer) ctx.getEnvironment().getRegionServerServices();
    HRegion userRegion = ctx.getEnvironment().getRegion();
    HTableDescriptor userTableDesc = userRegion.getTableDesc();
    String tableName = userTableDesc.getNameAsString();
    if (IndexUtils.isCatalogTable(userTableDesc.getName()) || IndexUtils.isIndexTable(tableName)) {
      return;
    }
    List<IndexSpecification> indices = indexManager.getIndicesForTable(tableName);
    if (indices == null || indices.isEmpty()) {
      LOG.trace("skipping preBatchMutate for the table " + tableName + " as there are no indices");
      return;
    }
    LOG.trace("Entering preBatchMutate for the table " + tableName);
    LOG.trace("Indices for the table " + tableName + " are: " + indices);
    HRegion indexRegion = getIndexTableRegion(tableName, userRegion, rs);
    // Storing this found HRegion in the index table within the thread locale.
    IndexEdits indexEdits = threadLocal.get();
    indexEdits.indexRegion = indexRegion;
    for (Pair<Mutation, OperationStatus> mutation : mutationVsBatchOp) {
      if (mutation.getSecond().getOperationStatusCode() != OperationStatusCode.NOT_RUN) {
        continue;
      }
      // only for successful puts
      Mutation m = mutation.getFirst();
      if (m instanceof Put) {
        try {
          prepareIndexMutations(indices, userRegion, m, tableName, indexRegion);
        } catch (IOException e) {
          mutation.setSecond(new OperationStatus(OperationStatusCode.SANITY_CHECK_FAILURE, e
              .getMessage()));
        }
      } else if (m instanceof Delete) {
        prepareIndexMutations(indices, userRegion, m, tableName, indexRegion);
      }
    }
    indexEdits.setUpdateLocked();
    indexRegion.updateLock();
    LOG.trace("Exiting preBatchMutate for the table " + tableName);
  }

  private HRegion getIndexTableRegion(String tableName, HRegion userRegion, HRegionServer rs)
      throws IOException {
    String indexTableName = IndexUtils.getIndexTableName(tableName);
    Collection<HRegion> idxTabRegions = rs.getOnlineRegions(Bytes.toBytes(indexTableName));
    for (HRegion idxTabRegion : idxTabRegions) {
      // TODO start key check is enough? May be we can check for the
      // possibility for N-1 Mapping?
      if (Bytes.equals(idxTabRegion.getStartKey(), userRegion.getStartKey())) {
        return idxTabRegion;
      }
    }
    // No corresponding index region found in the RS online regions list!
    LOG.warn("Index Region not found on the region server . "
        + "So skipping the put. Need Balancing");
    // TODO give a proper Exception msg
    throw new IOException();
  }

  private void prepareIndexMutations(List<IndexSpecification> indices, HRegion userRegion,
      Mutation mutation, String tableName, HRegion indexRegion) throws IOException {
    IndexEdits indexEdits = threadLocal.get();
    if (mutation instanceof Put) {
      for (IndexSpecification index : indices) {
        // Handle each of the index
        Mutation indexPut = IndexUtils.prepareIndexPut((Put) mutation, index, indexRegion);
        if (null != indexPut) {
          // This mutation can be null when the user table mutation is not
          // containing all of the indexed col value.
          indexEdits.add(indexPut);
        }
      }
    } else if (mutation instanceof Delete) {
      Collection<? extends Mutation> indexDeletes =
          prepareIndexDeletes((Delete) mutation, userRegion, indices, indexRegion);
      indexEdits.addAll(indexDeletes);
    } else {
      // TODO : Log or throw exception
    }
  }

  Collection<? extends Mutation> prepareIndexDeletes(Delete delete, HRegion userRegion,
      List<IndexSpecification> indexSpecs, HRegion indexRegion) throws IOException {
    Collection<Delete> indexDeletes = new LinkedHashSet<Delete>();
    for (Entry<byte[], List<KeyValue>> entry : delete.getFamilyMap().entrySet()) {
      for (KeyValue kv : entry.getValue()) {
        indexDeletes.addAll(getIndexDeletes(indexSpecs, userRegion, indexRegion, kv));
      }
    }
    return indexDeletes;
  }

  private static Collection<Delete> getIndexDeletes(List<IndexSpecification> indexSpecs,
      HRegion userRegion, HRegion indexRegion, KeyValue deleteKV) throws IOException {
    Collection<Delete> indexDeletes = new LinkedHashSet<Delete>();
    List<IndexSpecification> indicesToUpdate = new LinkedList<IndexSpecification>();
    Multimap<Long, KeyValue> groupedKV =
        doGetAndGroupByTS(indexSpecs, userRegion, deleteKV, indicesToUpdate);

    // There can be multiple index kvs for each user kv
    // So, prepare all resultant index delete kvs for this user delete kv
    for (Entry<Long, Collection<KeyValue>> entry : groupedKV.asMap().entrySet()) {
      for (IndexSpecification index : indicesToUpdate) {
        ByteArrayBuilder indexRow =
            IndexUtils.getIndexRowKeyHeader(index, indexRegion.getStartKey(), deleteKV.getRow());
        boolean update = false;
        for (ColumnQualifier cq : index.getIndexColumns()) {
          KeyValue kvFound = null;
          for (KeyValue kv : entry.getValue()) {
            if (Bytes.equals(cq.getColumnFamily(), kv.getFamily())
                && Bytes.equals(cq.getQualifier(), kv.getQualifier())) {
              kvFound = kv;
              update = true;
              break;
            }
          }
          if (kvFound == null) {
            indexRow.position(indexRow.position() + cq.getMaxValueLength());
          } else {
            IndexUtils.updateRowKeyForKV(cq, kvFound, indexRow);
          }
        }
        if (update) {
          // Append the actual row key at the end of the index row key.
          indexRow.put(deleteKV.getRow());
          Delete idxDelete = new Delete(indexRow.array());
          if (deleteKV.isDeleteType()) {
            idxDelete
                .deleteColumn(Constants.IDX_COL_FAMILY, Constants.IDX_COL_QUAL, entry.getKey());
          } else {
            idxDelete.deleteFamily(Constants.IDX_COL_FAMILY, entry.getKey());
          }
          idxDelete.setWriteToWAL(false);
          indexDeletes.add(idxDelete);
        }
      }
    }
    return indexDeletes;
  }

  private static Multimap<Long, KeyValue> doGetAndGroupByTS(List<IndexSpecification> indexSpecs,
      HRegion userRegion, KeyValue deleteKV, List<IndexSpecification> indicesToConsider)
      throws IOException {

    Get get = new Get(deleteKV.getRow());
    long maxTS = HConstants.LATEST_TIMESTAMP;

    if (deleteKV.getTimestamp() < maxTS) {
      // Add +1 to make the current get includes the timestamp
      maxTS = deleteKV.getTimestamp() + 1;
    }
    get.setTimeRange(HConstants.OLDEST_TIMESTAMP, maxTS);

    for (IndexSpecification index : indexSpecs) {
      // Get all indices involves this family/qualifier
      if (index.contains(deleteKV.getFamily(), deleteKV.getQualifier())) {
        indicesToConsider.add(index);
        for (ColumnQualifier cq : index.getIndexColumns()) {
          get.addColumn(cq.getColumnFamily(), cq.getQualifier());
        }
      }
    }
    if (deleteKV.isDeleteType()) {
      get.setMaxVersions(1);
    } else if (deleteKV.isDeleteColumnOrFamily()) {
      get.setMaxVersions();
    }
    List<KeyValue> userKVs = userRegion.get(get, 0).list();

    // Group KV based on timestamp
    Multimap<Long, KeyValue> groupedKV = HashMultimap.create();

    if (userKVs != null) {
      for (KeyValue userKV : userKVs) {
        groupedKV.put(userKV.getTimestamp(), userKV);
      }
    }
    return groupedKV;
  }

  // collection of edits for index table's memstore and WAL
  public static class IndexEdits {
    private WALEdit walEdit = new WALEdit();
    private HRegion indexRegion;
    private boolean updatesLocked = false;

    /**
     * Collection of mutations with locks. Locks will be null always as they not yet acquired for
     * index table.
     * @see HRegion#batchMutate(Pair[])
     */
    private List<Pair<Mutation, Integer>> mutations = new ArrayList<Pair<Mutation, Integer>>();

    public WALEdit getWALEdit() {
      return this.walEdit;
    }

    public boolean isUpdatesLocked() {
      return this.updatesLocked;
    }

    public void setUpdateLocked() {
      updatesLocked = true;
    }

    public void add(Mutation mutation) {
      // Check if WAL is disabled
      for (List<KeyValue> kvs : mutation.getFamilyMap().values()) {
        for (KeyValue kv : kvs) {
          this.walEdit.add(kv);
        }
      }
      // There is no lock acquired for index table. So, set it to null
      this.mutations.add(new Pair<Mutation, Integer>(mutation, null));
    }

    public void addAll(Collection<? extends Mutation> mutations) {
      for (Mutation mutation : mutations) {
        add(mutation);
      }
    }

    public List<Pair<Mutation, Integer>> getIndexMutations() {
      return this.mutations;
    }

    public HRegion getRegion() {
      return this.indexRegion;
    }
  }

  @Override
  public void postBatchMutate(final ObserverContext<RegionCoprocessorEnvironment> ctx,
      final List<Mutation> mutations, WALEdit walEdit) {
    HTableDescriptor userTableDesc = ctx.getEnvironment().getRegion().getTableDesc();
    String tableName = userTableDesc.getNameAsString();
    if (IndexUtils.isCatalogTable(userTableDesc.getName()) || IndexUtils.isIndexTable(tableName)) {
      return;
    }
    List<IndexSpecification> indices = indexManager.getIndicesForTable(tableName);
    if (indices == null || indices.isEmpty()) {
      LOG.trace("skipping postBatchMutate for the table " + tableName + " as there are no indices");
      return;
    }
    LOG.trace("Entering postBatchMutate for the table " + tableName);
    IndexEdits indexEdits = threadLocal.get();
    List<Pair<Mutation, Integer>> indexMutations = indexEdits.getIndexMutations();

    if (indexMutations.size() == 0) {
      return;
    }
    HRegion hr = indexEdits.getRegion();
    LOG.trace("Updating index table " + hr.getRegionInfo().getTableNameAsString());
    try {
      hr.batchMutateForIndex(indexMutations.toArray(new Pair[indexMutations.size()]));
    } catch (IOException e) {
      // TODO This can come? If so we need to revert the actual put
      // and make the op failed.
      LOG.error("Error putting data into the index region", e);
    }
    LOG.trace("Exiting postBatchMutate for the table " + tableName);
  }

  @Override
  public void postCompleteBatchMutate(final ObserverContext<RegionCoprocessorEnvironment> ctx,
      List<Mutation> mutations) throws IOException {
    IndexEdits indexEdits = threadLocal.get();
    if (indexEdits != null) {
      if (indexEdits.isUpdatesLocked()) {
        indexEdits.getRegion().releaseLock();
      }

    }
    threadLocal.remove();

  }

  @Override
  public boolean postScannerFilterRow(ObserverContext<RegionCoprocessorEnvironment> e,
      InternalScanner s, byte[] currentRow, boolean hasMore) throws IOException {
    String tableName = e.getEnvironment().getRegion().getTableDesc().getNameAsString();
    if (IndexUtils.isIndexTable(tableName)) {
      return true;
    }
    SeekAndReadRegionScanner bsrs = SeekAndReadRegionScannerHolder.getRegionScanner();
    if (bsrs != null) {
      while (false == bsrs.seekToNextPoint()) {
        SeekPointFetcher seekPointFetcher = scannerMap.get(bsrs);
        if (null != seekPointFetcher) {
          List<byte[]> seekPoints = new ArrayList<byte[]>(1);
          seekPointFetcher.nextSeekPoints(seekPoints, 1);
          // TODO use return boolean?
          if (seekPoints.isEmpty()) {
            LOG.trace("No seekpoints are remaining hence returning..  ");
            return false;
          }
          bsrs.addSeekPoints(seekPoints);
          if (isTestingEnabled) {
            setSeekPoints(seekPoints);
            setSeekpointAdded(true);
            addSeekPoints(seekPoints);
          }
        } else {
          // This will happen for a region with no index
          break;
        }
      }
    }
    return true;
 

  public RegionScanner postScannerOpen(ObserverContext<RegionCoprocessorEnvironment> e, Scan scan,
      RegionScanner s) {
    HRegion region = e.getEnvironment().getRegion();
    String tableName = region.getTableDesc().getNameAsString();
    HRegionServer rs = (HRegionServer) e.getEnvironment().getRegionServerServices();
    // If the passed region is a region from an indexed table
    SeekAndReadRegionScanner bsrs = null;

    try {
      List<IndexSpecification> indexlist = IndexManager.getInstance().getIndicesForTable(tableName);
      if (indexlist != null) {
        if (indexlist == null || indexlist.isEmpty()) {
          // Not an indexed table. Just return.
          return s;
        }
        LOG.trace("Entering postScannerOpen for the table " + tableName);
        Collection<HRegion> onlineRegions = rs.getOnlineRegionsLocalContext();
        for (HRegion onlineIdxRegion : onlineRegions) {
          if (IndexUtils.isCatalogTable(Bytes.toBytes(onlineIdxRegion.getTableDesc()
              .getNameAsString()))) {
            continue;
          }
          if (onlineIdxRegion.equals(region)) {
            continue;
          }
          if (Bytes.equals(onlineIdxRegion.getStartKey(), region.getStartKey())
              && Bytes.equals(Bytes.toBytes(IndexUtils.getIndexTableName(region.getTableDesc()
                  .getNameAsString())), onlineIdxRegion.getTableDesc().getName())) {
            ScanFilterEvaluator mapper = new ScanFilterEvaluator();
            IndexRegionScanner indexScanner =
                mapper.evaluate(scan, indexlist, onlineIdxRegion.getStartKey(), onlineIdxRegion,
                  tableName);
            if (indexScanner == null) return s;
            SeekPointFetcher spf = new SeekPointFetcher(indexScanner);
            ReInitializableRegionScanner reinitializeScanner =
                new ReInitializableRegionScannerImpl(s, scan, spf);
            bsrs = new BackwardSeekableRegionScanner(reinitializeScanner, scan, region, null);
            scannerMap.put(bsrs, spf);
            LOG.trace("Scanner Map has " + scannerMap);
            break;
          }
        }
        LOG.trace("Exiting postScannerOpen for the table " + tableName);
      }
    } catch (Exception ex) {
      LOG.error("Exception occured in postScannerOpen for the table " + tableName, ex);
    }
    if (bsrs != null) {
      return bsrs;
    } else {
      return s;
    }
  }

  public boolean preScannerNext(ObserverContext<RegionCoprocessorEnvironment> e, InternalScanner s,
      List<Result> results, int nbRows, boolean hasMore) throws IOException {
    HRegion region = e.getEnvironment().getRegion();
    String tableName = region.getTableDesc().getNameAsString();
    try {
      if (s instanceof SeekAndReadRegionScanner) {
        LOG.trace("Entering preScannerNext for the table " + tableName);
        BackwardSeekableRegionScanner bsrs = (BackwardSeekableRegionScanner) s;
        SeekAndReadRegionScannerHolder.setRegionScanner(bsrs);
        SeekPointFetcher spf = scannerMap.get(bsrs);
        List<byte[]> seekPoints = null;
        if (spf != null) {
          if (isTestingEnabled) {
            setIndexedFlowUsed(true);
          }
          seekPoints = new ArrayList<byte[]>();
          spf.nextSeekPoints(seekPoints, nbRows);
        }
        if (seekPoints == null || seekPoints.isEmpty()) {
          LOG.trace("No seekpoints are remaining hence returning..  ");
          SeekAndReadRegionScannerHolder.removeRegionScanner();
          e.bypass();
          return false;
        }
        bsrs.addSeekPoints(seekPoints);
        // This setting is just for testing purpose
        if (isTestingEnabled) {
          setSeekPoints(seekPoints);
          setSeekpointAdded(true);
          addSeekPoints(seekPoints);
        }
        LOG.trace("Exiting preScannerNext for the table " + tableName);
      }
    } catch (Exception ex) {
      LOG.error("Exception occured in preScannerNext for the table " + tableName + ex);
    }
    return true;
  }

  @Override
  public boolean postScannerNext(ObserverContext<RegionCoprocessorEnvironment> e,
      InternalScanner s, List<Result> results, int limit, boolean hasMore) throws IOException {
    if (s instanceof SeekAndReadRegionScanner) {
      SeekAndReadRegionScannerHolder.removeRegionScanner();
    }
    return true;
  }

  @Override
  public void preScannerClose(ObserverContext<RegionCoprocessorEnvironment> e, InternalScanner s)
      throws IOException {
    if (s instanceof BackwardSeekableRegionScanner) {
      scannerMap.remove((RegionScanner) s);
    }
  }

  @Override
  public SplitInfo preSplitBeforePONR(ObserverContext<RegionCoprocessorEnvironment> e,
      byte[] splitKey) throws IOException {
    RegionCoprocessorEnvironment environment = e.getEnvironment();
    HRegionServer rs = (HRegionServer) environment.getRegionServerServices();
    HRegion region = environment.getRegion();
    String userTableName = region.getTableDesc().getNameAsString();
    LOG.trace("Entering preSplitBeforePONR for the table " + userTableName + " for the region "
        + region.getRegionInfo());
    String indexTableName = IndexUtils.getIndexTableName(userTableName);
    if (indexManager.getIndicesForTable(userTableName) != null) {
      HRegion indexRegion = null;
      SplitTransaction st = null;
      try {
        indexRegion = getIndexRegion(rs, region.getStartKey(), indexTableName);
        if (null != indexRegion) {
          LOG.info("Flushing the cache for the index table " + indexTableName + " for the region "
              + indexRegion.getRegionInfo());
          indexRegion.flushcache();
          if (LOG.isInfoEnabled()) {
            LOG.info("Forcing split for the index table " + indexTableName + " with split key "
                + Bytes.toString(splitKey));
          }
          st = new SplitTransaction(indexRegion, splitKey);
          if (!st.prepare()) {
            LOG.error("Prepare for the index table " + indexTableName
                + " failed. So returning null. ");
            return null;
          }
          indexRegion.forceSplit(splitKey);
          PairOfSameType<HRegion> daughterRegions = st.stepsBeforeAddingPONR(rs, rs, false);
          SplitInfo splitInfo = splitThreadLocal.get();
          splitInfo.setDaughtersAndTransaction(daughterRegions, st);
          LOG.info("Daughter regions created for the index table " + indexTableName
              + " for the region " + indexRegion.getRegionInfo());
          return splitInfo;
        } else {
          LOG.error("IndexRegion for the table " + indexTableName + " is null. So returning null. ");
          return null;
        }
      } catch (Exception ex) {
        LOG.error("Error while spliting the indexTabRegion or not able to get the indexTabRegion:"
            + indexRegion != null ? indexRegion.getRegionName() : "", ex);
        st.rollback(rs, rs);
        return null;
      }
    }
    LOG.trace("Indexes for the table " + userTableName
        + " are null. So returning the empty SplitInfo");
    return new SplitInfo();
  }

  @Override
  public void preSplit(ObserverContext<RegionCoprocessorEnvironment> e) throws IOException {
    if (splitThreadLocal != null) {
      splitThreadLocal.remove();
      splitThreadLocal.set(new SplitInfo());
    }
  }

  private HRegion getIndexRegion(HRegionServer rs, byte[] startKey, String indexTableName)
      throws IOException {
    List<HRegion> indexTabRegions = rs.getOnlineRegions(Bytes.toBytes(indexTableName));
    for (HRegion indexRegion : indexTabRegions) {
      if (Bytes.equals(startKey, indexRegion.getStartKey())) {
        return indexRegion;
      }
    }
    return null;
  }

  @Override
  public void preSplitAfterPONR(ObserverContext<RegionCoprocessorEnvironment> ctx)
      throws IOException {
    RegionCoprocessorEnvironment environment = ctx.getEnvironment();
    HRegionServer rs = (HRegionServer) environment.getRegionServerServices();
    HRegion region = environment.getRegion();
    String userTableName = region.getTableDesc().getNameAsString();
    String indexTableName = IndexUtils.getIndexTableName(userTableName);
    if (IndexUtils.isIndexTable(userTableName)) {
      return;
    }
    LOG.trace("Entering postSplit for the table " + userTableName + " for the region "
        + region.getRegionInfo());
    IndexManager indexManager = IndexManager.getInstance();
    SplitTransaction splitTransaction = null;
    if (indexManager.getIndicesForTable(userTableName) != null) {
      try {
        SplitInfo splitInfo = splitThreadLocal.get();
        splitTransaction = splitInfo.getSplitTransaction();
        PairOfSameType<HRegion> daughters = splitInfo.getDaughters();
        if (splitTransaction != null && daughters != null) {
          splitTransaction.stepsAfterPONR(rs, rs, daughters);
          LOG.info("Daughter regions are opened and split transaction finished"
              + " for zknodes for index table " + indexTableName + " for the region "
              + region.getRegionInfo());
        }
      } catch (Exception ex) {
        String msg =
            "Splitting of index region has failed in stepsAfterPONR stage so aborting the server";
        LOG.error(msg, ex);
        rs.abort(msg);
      }
    }
  }

  // A thread local variable used to get the splitted region information of the index region.
  // This is needed becuase in order to do the PONR entry we need the info of the index
  // region's daughter entries.
  public static final ThreadLocal<SplitInfo> splitThreadLocal = new ThreadLocal<SplitInfo>() {
    protected SplitInfo initialValue() {
      return null;
    };
  };

  @Override
  public void preRollBack(ObserverContext<RegionCoprocessorEnvironment> ctx) throws IOException {
    RegionCoprocessorEnvironment environment = ctx.getEnvironment();
    HRegionServer rs = (HRegionServer) environment.getRegionServerServices();
    HRegion region = environment.getRegion();
    String userTableName = region.getTableDesc().getNameAsString();
    if (IndexUtils.isIndexTable(userTableName)) {
      return;
    }
    LOG.trace("Entering preRollBack for the table " + userTableName + " for the region "
        + region.getRegionInfo());
    SplitInfo splitInfo = splitThreadLocal.get();
    SplitTransaction splitTransaction = splitInfo.getSplitTransaction();
    try {
      if (splitTransaction != null) {
        splitTransaction.rollback(rs, rs);
        LOG.info("preRollBack successfully done for the table " + userTableName
            + " for the region " + region.getRegionInfo());
      }
    } catch (Exception e) {
      LOG.error(
        "Error while rolling back the split failure for index region "
            + splitTransaction.getParent(), e);
      rs.abort("Abort; we got an error during rollback of index");
    }
  }

  // For testing to check whether final step of seek point is added
  public static void setSeekpointAdded(boolean isSeekpointAddded) {
    IndexRegionObserver.isSeekpointAddded = isSeekpointAddded;
  }

  // For testing
  public static boolean getSeekpointAdded() {
    return isSeekpointAddded;
  }

  // For testing to ensure indexed flow is used or not
  public static void setIndexedFlowUsed(boolean isIndexedFlowUsed) {
    IndexRegionObserver.isIndexedFlowUsed = isIndexedFlowUsed;
  }

  // For testing
  public static boolean getIndexedFlowUsed() {
    return isIndexedFlowUsed;
  }

  // For testing
  static List<byte[]> getSeekpoints() {
    return seekPoints;
  }

  // For testing to ensure cache size is returned correctly
  public static void setSeekPoints(List<byte[]> seekPoints) {
    IndexRegionObserver.seekPoints = seekPoints;
  }

  public static void setIsTestingEnabled(boolean isTestingEnabled) {
    IndexRegionObserver.isTestingEnabled = isTestingEnabled;
  }

  public static void addSeekPoints(List<byte[]> seekPoints) {
    if (seekPoints == null) {
      IndexRegionObserver.seekPointsForMultipleIndices = null;
      return;
    }
    if (IndexRegionObserver.seekPointsForMultipleIndices == null) {
      IndexRegionObserver.seekPointsForMultipleIndices = new ArrayList<byte[]>();
    }
    IndexRegionObserver.seekPointsForMultipleIndices.addAll(seekPoints);
  }

  public static List<byte[]> getMultipleSeekPoints() {
    return IndexRegionObserver.seekPointsForMultipleIndices;
  }

  private static class SeekAndReadRegionScannerHolder {
    private static ThreadLocal<SeekAndReadRegionScanner> holder =
        new ThreadLocal<SeekAndReadRegionScanner>();

    public static void setRegionScanner(SeekAndReadRegionScanner scanner) {
      holder.set(scanner);
    }

    public static SeekAndReadRegionScanner getRegionScanner() {
      return holder.get();
    }

    public static void removeRegionScanner() {
      holder.remove();
    }
  }

  @Override
  public InternalScanner preCompactScannerOpen(ObserverContext<RegionCoprocessorEnvironment> c,
      Store store, List<? extends KeyValueScanner> scanners, ScanType scanType, long earliestPutTs,
      InternalScanner s) throws IOException {
    HRegionServer rs = (HRegionServer) c.getEnvironment().getRegionServerServices();
    if (!store.getTableName().contains(Constants.INDEX_TABLE_SUFFIX)) {
      // Not an index table
      return null;
    }
    long smallestReadPoint = c.getEnvironment().getRegion().getSmallestReadPoint();
    String actualTableName = IndexUtils.getActualTableNameFromIndexTableName(store.getTableName());
    TTLStoreScanner ttlStoreScanner =
        new TTLStoreScanner(store, smallestReadPoint, earliestPutTs, scanType, scanners,
            new TTLExpiryChecker(), actualTableName, rs);
    return ttlStoreScanner;
  }

  @Override
  public void postClose(ObserverContext<RegionCoprocessorEnvironment> e, boolean abortRequested) {
    HRegion region = e.getEnvironment().getRegion();
    byte[] tableName = region.getRegionInfo().getTableName();
    if (IndexUtils.isCatalogTable(tableName) || IndexUtils.isIndexTable(tableName)) {
      return;
    }
    if (splitThreadLocal.get() == null) {
      this.indexManager.decrementRegionCount(Bytes.toString(tableName), true);
    } else {
      this.indexManager.decrementRegionCount(Bytes.toString(tableName), false);
    }
  }

  private boolean isValidIndexMutation(HTableDescriptor userTableDesc, String tableName) {
    if (IndexUtils.isCatalogTable(userTableDesc.getName()) || IndexUtils.isIndexTable(tableName)) {
      return false;
    }
    List<IndexSpecification> indices = indexManager.getIndicesForTable(tableName);
    if (indices == null || indices.isEmpty()) {
      LOG.trace("skipping preBatchMutate for the table " + tableName + " as there are no indices");
      return false;
    }
    return true;
  }

  private void acquireLockOnIndexRegion(String tableName, HRegion userRegion, HRegionServer rs)
      throws IOException {
    HRegion indexRegion = getIndexTableRegion(tableName, userRegion, rs);
    indexRegion.checkResources();
    indexRegion.startRegionOperation();
  }

  @Override
  public void postCloseRegionOperation(ObserverContext<RegionCoprocessorEnvironment> e)
      throws IOException {
    HRegionServer rs = (HRegionServer) e.getEnvironment().getRegionServerServices();
    HRegion userRegion = e.getEnvironment().getRegion();
    HTableDescriptor userTableDesc = userRegion.getTableDesc();
    String tableName = userTableDesc.getNameAsString();
    if (!isValidIndexMutation(userTableDesc, tableName)) {
      // Ideally need not release any lock because in the preStartRegionOperationHook we would not
      // have
      // acquired
      // any lock on the index region
      return;
    }
    HRegion indexRegion = getIndexTableRegion(tableName, userRegion, rs);
    // This check for isClosed and isClosing is needed because we should not unlock
    // when the index region lock would have already been released before throwing NSRE

    // TODO : What is the scenario that i may get an IllegalMonitorStateException
    if (!indexRegion.isClosed() || !indexRegion.isClosing()) {
      indexRegion.closeRegionOperation();
    }
  }

  @Override
  public void postStartRegionOperation(ObserverContext<RegionCoprocessorEnvironment> e)
      throws IOException {
    HRegionServer rs = (HRegionServer) e.getEnvironment().getRegionServerServices();
    HRegion userRegion = e.getEnvironment().getRegion();
    HTableDescriptor userTableDesc = userRegion.getTableDesc();
    String tableName = userTableDesc.getNameAsString();
    if (!isValidIndexMutation(userTableDesc, tableName)) {
      return;
    }
    acquireLockOnIndexRegion(tableName, userRegion, rs);

  }

}
TOP

Related Classes of org.apache.hadoop.hbase.index.coprocessor.regionserver.IndexRegionObserver

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.