All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.hadoop.hbase.namespace.NamespaceStateManager Maven / Gradle / Ivy

There is a newer version: 3.0.0-beta-1
Show newest version
/**
 * 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.namespace;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.NamespaceDescriptor;
import org.apache.hadoop.hbase.RegionTransition;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.hbase.client.MetaScanner;
import org.apache.hadoop.hbase.exceptions.DeserializationException;
import org.apache.hadoop.hbase.executor.EventType;
import org.apache.hadoop.hbase.master.MasterServices;
import org.apache.hadoop.hbase.master.TableNamespaceManager;
import org.apache.hadoop.hbase.quotas.QuotaExceededException;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.zookeeper.ZKAssign;
import org.apache.hadoop.hbase.zookeeper.ZKUtil;
import org.apache.hadoop.hbase.zookeeper.ZooKeeperListener;
import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.data.Stat;

/**
 * NamespaceStateManager manages state (in terms of quota) of all the namespaces. It contains a
 * cache which is updated based on the hooks in the NamespaceAuditor class.
 */
@InterfaceAudience.Private
class NamespaceStateManager extends ZooKeeperListener {

  private static final Log LOG = LogFactory.getLog(NamespaceStateManager.class);
  private ConcurrentMap nsStateCache;
  private MasterServices master;
  private volatile boolean initialized = false;

  public NamespaceStateManager(MasterServices masterServices, ZooKeeperWatcher zkw) {
    super(zkw);
    nsStateCache = new ConcurrentHashMap();
    master = masterServices;
  }

  /**
   * Starts the NamespaceStateManager. The boot strap of cache is done in the post master start hook
   * of the NamespaceAuditor class.
   * @throws IOException Signals that an I/O exception has occurred.
   */
  public void start() throws IOException {
    LOG.info("Namespace State Manager started.");
    initialize();
    watcher.registerListenerFirst(this);
  }

  /**
   * Gets an instance of NamespaceTableAndRegionInfo associated with namespace.
   * @param The name of the namespace
   * @return An instance of NamespaceTableAndRegionInfo.
   */
  public NamespaceTableAndRegionInfo getState(String name) {
    return nsStateCache.get(name);
  }

  /**
   * Check if adding a region violates namespace quota, if not update namespace cache.
   * @param TableName
   * @param regionName
   * @param incr
   * @return true, if region can be added to table.
   * @throws IOException Signals that an I/O exception has occurred.
   */
  synchronized boolean checkAndUpdateNamespaceRegionCount(TableName name, byte[] regionName,
      int incr) throws IOException {
    String namespace = name.getNamespaceAsString();
    NamespaceDescriptor nspdesc = getNamespaceDescriptor(namespace);
    if (nspdesc != null) {
      NamespaceTableAndRegionInfo currentStatus;
      currentStatus = getState(namespace);
      if (incr > 0
          && currentStatus.getRegionCount() >= TableNamespaceManager.getMaxRegions(nspdesc)) {
        LOG.warn("The region " + Bytes.toStringBinary(regionName)
            + " cannot be created. The region count  will exceed quota on the namespace. "
            + "This may be transient, please retry later if there are any ongoing split"
            + " operations in the namespace.");
        return false;
      }
      NamespaceTableAndRegionInfo nsInfo = nsStateCache.get(namespace);
      if (nsInfo != null) {
        nsInfo.incRegionCountForTable(name, incr);
      } else {
        LOG.warn("Namespace state found null for namespace : " + namespace);
      }
    }
    return true;
  }
  
  /**
   * Check and update region count for an existing table. To handle scenarios like restore snapshot
   * @param TableName name of the table for region count needs to be checked and updated
   * @param incr count of regions
   * @throws QuotaExceededException if quota exceeds for the number of regions allowed in a
   *           namespace
   * @throws IOException Signals that an I/O exception has occurred.
   */
  synchronized void checkAndUpdateNamespaceRegionCount(TableName name, int incr) 
      throws IOException {
    String namespace = name.getNamespaceAsString();
    NamespaceDescriptor nspdesc = getNamespaceDescriptor(namespace);
    if (nspdesc != null) {
      NamespaceTableAndRegionInfo currentStatus = getState(namespace);
      int regionCountOfTable = currentStatus.getRegionCountOfTable(name);
      if ((currentStatus.getRegionCount() - regionCountOfTable + incr) > TableNamespaceManager
          .getMaxRegions(nspdesc)) {
        throw new QuotaExceededException("The table " + name.getNameAsString()
            + " region count cannot be updated as it would exceed maximum number "
            + "of regions allowed in the namespace.  The total number of regions permitted is "
            + TableNamespaceManager.getMaxRegions(nspdesc));
      }
      currentStatus.removeTable(name);
      currentStatus.addTable(name, incr);
    }
  }

  private NamespaceDescriptor getNamespaceDescriptor(String namespaceAsString) {
    try {
      return this.master.getNamespaceDescriptor(namespaceAsString);
    } catch (IOException e) {
      LOG.error("Error while fetching namespace descriptor for namespace : " + namespaceAsString);
      return null;
    }
  }

  synchronized void checkAndUpdateNamespaceTableCount(TableName table, int numRegions)
      throws IOException {
    String namespace = table.getNamespaceAsString();
    NamespaceDescriptor nspdesc = getNamespaceDescriptor(namespace);
    if (nspdesc != null) {
      NamespaceTableAndRegionInfo currentStatus;
      currentStatus = getState(nspdesc.getName());
      if ((currentStatus.getTables().size()) >= TableNamespaceManager.getMaxTables(nspdesc)) {
        throw new QuotaExceededException("The table " + table.getNameAsString()
            + " cannot be created as it would exceed maximum number of tables allowed "
            + " in the namespace.  The total number of tables permitted is "
            + TableNamespaceManager.getMaxTables(nspdesc));
      }
      if ((currentStatus.getRegionCount() + numRegions) > TableNamespaceManager
          .getMaxRegions(nspdesc)) {
        throw new QuotaExceededException("The table " + table.getNameAsString()
            + " is not allowed to have " + numRegions
            + " regions. The total number of regions permitted is only "
            + TableNamespaceManager.getMaxRegions(nspdesc) + ", while current region count is "
            + currentStatus.getRegionCount()
            + ". This may be transient, please retry later if there are any"
            + " ongoing split operations in the namespace.");
      }
    } else {
      throw new IOException("Namespace Descriptor found null for " + namespace
          + " This is unexpected.");
    }
    addTable(table, numRegions);
  }

  NamespaceTableAndRegionInfo addNamespace(String namespace) {
    if (!nsStateCache.containsKey(namespace)) {
      NamespaceTableAndRegionInfo a1 = new NamespaceTableAndRegionInfo(namespace);
      nsStateCache.put(namespace, a1);
    }
    return nsStateCache.get(namespace);
  }

  /**
   * Delete the namespace state.
   * @param An instance of NamespaceTableAndRegionInfo
   */
  void deleteNamespace(String namespace) {
    this.nsStateCache.remove(namespace);
  }

  private void addTable(TableName tableName, int regionCount) throws IOException {
    NamespaceTableAndRegionInfo info = nsStateCache.get(tableName.getNamespaceAsString());
    if (info != null) {
      info.addTable(tableName, regionCount);
    } else {
      throw new IOException("Bad state : Namespace quota information not found for namespace : "
          + tableName.getNamespaceAsString());
    }
  }

  synchronized void removeTable(TableName tableName) {
    NamespaceTableAndRegionInfo info = nsStateCache.get(tableName.getNamespaceAsString());
    if (info != null) {
      info.removeTable(tableName);
    }
  }

  /**
   * Initialize namespace state cache by scanning meta table.
   */
  private void initialize() throws IOException {
    List namespaces = this.master.listNamespaceDescriptors();
    for (NamespaceDescriptor namespace : namespaces) {
      addNamespace(namespace.getName());
      List tables = this.master.listTableNamesByNamespace(namespace.getName());
      for (TableName table : tables) {
        if (table.isSystemTable()) {
          continue;
        }
        int regionCount = 0;
        Map regions =
            MetaScanner.allTableRegions(this.master.getConnection(), table);
        for (HRegionInfo info : regions.keySet()) {
          if (!info.isSplit()) {
            regionCount++;
          }
        }
        addTable(table, regionCount);
      }
    }
    LOG.info("Finished updating state of " + nsStateCache.size() + " namespaces. ");
    initialized = true;
  }

  boolean isInitialized() {
    return initialized;
  }

  public synchronized void removeRegionFromTable(HRegionInfo hri) throws IOException {
    String namespace = hri.getTable().getNamespaceAsString();
    NamespaceTableAndRegionInfo nsInfo = nsStateCache.get(namespace);
    if (nsInfo != null) {
      nsInfo.decrementRegionCountForTable(hri.getTable(), 1);
    } else {
      throw new IOException("Namespace state found null for namespace : " + namespace);
    }
  }

  @Override
  public void nodeCreated(String path) {
    checkSplittingOrMergingNode(path);
  }

  @Override
  public void nodeChildrenChanged(String path) {
    checkSplittingOrMergingNode(path);
  }

  private void checkSplittingOrMergingNode(String path) {
    String msg = "Error reading data from zookeeper, ";
    try {
      if (path.startsWith(watcher.assignmentZNode)) {
        List children =
            ZKUtil.listChildrenAndWatchForNewChildren(watcher, watcher.assignmentZNode);
        if (children != null) {
          for (String child : children) {
            Stat stat = new Stat();
            byte[] data =
                ZKAssign.getDataAndWatch(watcher, ZKUtil.joinZNode(watcher.assignmentZNode, child),
                  stat);
            if (data != null) {
              RegionTransition rt = RegionTransition.parseFrom(data);
              if (rt.getEventType().equals(EventType.RS_ZK_REQUEST_REGION_SPLIT)) {
                TableName table = HRegionInfo.getTable(rt.getRegionName());
                if (!checkAndUpdateNamespaceRegionCount(table, rt.getRegionName(), 1)) {
                  ZKUtil.deleteNode(watcher, ZKUtil.joinZNode(watcher.assignmentZNode, child));
                }
              } else if (rt.getEventType().equals(EventType.RS_ZK_REQUEST_REGION_MERGE)) {
                TableName table = HRegionInfo.getTable(rt.getRegionName());
                checkAndUpdateNamespaceRegionCount(table, rt.getRegionName(), -1);
              }
            }
          }
        }
      }
    } catch (KeeperException ke) {
      LOG.error(msg, ke);
      watcher.abort(msg, ke);
    } catch (DeserializationException e) {
      LOG.error(msg, e);
      watcher.abort(msg, e);
    } catch (IOException e) {
      LOG.error(msg, e);
      watcher.abort(msg, e);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy