You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by ji...@apache.org on 2008/07/30 03:45:45 UTC
svn commit: r680902 [2/2] - in /hadoop/hbase/trunk: CHANGES.txt
conf/hbase-default.xml src/java/org/apache/hadoop/hbase/HConstants.java
src/java/org/apache/hadoop/hbase/client/HBaseAdmin.java
src/java/org/apache/hadoop/hbase/client/HConnectionManager.java
Modified: hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/client/HConnectionManager.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/client/HConnectionManager.java?rev=680902&r1=680901&r2=680902&view=diff
==============================================================================
--- hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/client/HConnectionManager.java (original)
+++ hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/client/HConnectionManager.java Tue Jul 29 18:45:44 2008
@@ -1,901 +1,908 @@
-/**
- * Copyright 2007 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.lang.reflect.UndeclaredThrowableException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.hadoop.hbase.DoNotRetryIOException;
-import org.apache.hadoop.hbase.HBaseConfiguration;
-import org.apache.hadoop.hbase.HConstants;
-import org.apache.hadoop.hbase.HRegionInfo;
-import org.apache.hadoop.hbase.HRegionLocation;
-import org.apache.hadoop.hbase.HServerAddress;
-import org.apache.hadoop.hbase.HTableDescriptor;
-import org.apache.hadoop.hbase.LocalHBaseCluster;
-import org.apache.hadoop.hbase.MasterNotRunningException;
-import org.apache.hadoop.hbase.RemoteExceptionHandler;
-import org.apache.hadoop.hbase.TableNotFoundException;
-import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor;
-import org.apache.hadoop.hbase.io.Cell;
-import org.apache.hadoop.hbase.io.RowResult;
-import org.apache.hadoop.hbase.ipc.HMasterInterface;
-import org.apache.hadoop.hbase.ipc.HRegionInterface;
-import org.apache.hadoop.hbase.ipc.HbaseRPC;
-import org.apache.hadoop.hbase.util.Bytes;
-import org.apache.hadoop.hbase.util.SoftSortedMap;
-import org.apache.hadoop.hbase.util.Writables;
-import org.apache.hadoop.ipc.RemoteException;
-
-/**
- * A non-instantiable class that manages connections to multiple tables in
- * multiple HBase instances.
- *
- * Used by {@link HTable} and {@link HBaseAdmin}
- */
-public class HConnectionManager implements HConstants {
- /*
- * Private. Not instantiable.
- */
- private HConnectionManager() {
- super();
- }
-
- // A Map of master HServerAddress -> connection information for that instance
- // Note that although the Map is synchronized, the objects it contains
- // are mutable and hence require synchronized access to them
- private static final Map<String, TableServers> HBASE_INSTANCES =
- new ConcurrentHashMap<String, TableServers>();
-
- /**
- * Get the connection object for the instance specified by the configuration
- * If no current connection exists, create a new connection for that instance
- * @param conf
- * @return HConnection object for the instance specified by the configuration
- */
- public static HConnection getConnection(HBaseConfiguration conf) {
- TableServers connection;
- synchronized (HBASE_INSTANCES) {
- String instanceName = conf.get(HBASE_DIR);
- connection = HBASE_INSTANCES.get(instanceName);
- if (connection == null) {
- connection = new TableServers(conf);
- HBASE_INSTANCES.put(instanceName, connection);
- }
- }
- return connection;
- }
-
- /**
- * Delete connection information for the instance specified by the configuration
- * @param conf
- */
- public static void deleteConnectionInfo(HBaseConfiguration conf) {
- synchronized (HBASE_INSTANCES) {
- HBASE_INSTANCES.remove(conf.get(HBASE_DIR));
- }
- }
-
- /**
- * Clear the static map of connection info.
- */
- public static void deleteConnectionInfo() {
- synchronized (HBASE_INSTANCES) {
- HBASE_INSTANCES.clear();
- }
- }
-
-
- /* Encapsulates finding the servers for an HBase instance */
- private static class TableServers implements HConnection, HConstants {
- private static final Log LOG = LogFactory.getLog(TableServers.class);
- private final Class<? extends HRegionInterface> serverInterfaceClass;
- private final long pause;
- private final int numRetries;
- private final int maxRPCAttempts;
-
- private final Integer masterLock = new Integer(0);
- private volatile boolean closed;
- private volatile HMasterInterface master;
- private volatile boolean masterChecked;
-
- private final Integer rootRegionLock = new Integer(0);
- private final Integer metaRegionLock = new Integer(0);
- private final Integer userRegionLock = new Integer(0);
-
- private volatile HBaseConfiguration conf;
-
- // Known region HServerAddress.toString() -> HRegionInterface
- private final Map<String, HRegionInterface> servers =
- new ConcurrentHashMap<String, HRegionInterface>();
-
- private HRegionLocation rootRegionLocation;
-
- private final Map<Integer, SoftSortedMap<byte [], HRegionLocation>>
- cachedRegionLocations = Collections.synchronizedMap(
- new HashMap<Integer, SoftSortedMap<byte [], HRegionLocation>>());
-
- /**
- * constructor
- * @param conf Configuration object
- */
- @SuppressWarnings("unchecked")
- public TableServers(HBaseConfiguration conf) {
- this.conf = LocalHBaseCluster.doLocal(new HBaseConfiguration(conf));
-
- String serverClassName =
- conf.get(REGION_SERVER_CLASS, DEFAULT_REGION_SERVER_CLASS);
-
- this.closed = false;
-
- try {
- this.serverInterfaceClass =
- (Class<? extends HRegionInterface>) Class.forName(serverClassName);
-
- } catch (ClassNotFoundException e) {
- throw new UnsupportedOperationException(
- "Unable to find region server interface " + serverClassName, e);
- }
-
- this.pause = conf.getLong("hbase.client.pause", 30 * 1000);
- this.numRetries = conf.getInt("hbase.client.retries.number", 5);
- this.maxRPCAttempts = conf.getInt("hbase.client.rpc.maxattempts", 1);
-
- this.master = null;
- this.masterChecked = false;
- }
-
- /** {@inheritDoc} */
- public HMasterInterface getMaster() throws MasterNotRunningException {
- HServerAddress masterLocation = null;
- synchronized (this.masterLock) {
- for (int tries = 0;
- !this.closed &&
- !this.masterChecked && this.master == null &&
- tries < numRetries;
- tries++) {
-
- masterLocation = new HServerAddress(this.conf.get(MASTER_ADDRESS,
- DEFAULT_MASTER_ADDRESS));
- try {
- HMasterInterface tryMaster = (HMasterInterface)HbaseRPC.getProxy(
- HMasterInterface.class, HMasterInterface.versionID,
- masterLocation.getInetSocketAddress(), this.conf);
-
- if (tryMaster.isMasterRunning()) {
- this.master = tryMaster;
- break;
- }
-
- } catch (IOException e) {
- if(tries == numRetries - 1) {
- // This was our last chance - don't bother sleeping
- break;
- }
- LOG.info("Attempt " + tries + " of " + this.numRetries +
- " failed with <" + e + ">. Retrying after sleep of " + this.pause);
- }
-
- // We either cannot connect to master or it is not running. Sleep & retry
-
- try {
- Thread.sleep(this.pause);
- } catch (InterruptedException e) {
- // continue
- }
- }
- this.masterChecked = true;
- }
- if (this.master == null) {
- if (masterLocation == null) {
- throw new MasterNotRunningException();
- }
- throw new MasterNotRunningException(masterLocation.toString());
- }
- return this.master;
- }
-
- /** {@inheritDoc} */
- public boolean isMasterRunning() {
- if (this.master == null) {
- try {
- getMaster();
-
- } catch (MasterNotRunningException e) {
- return false;
- }
- }
- return true;
- }
-
- /** {@inheritDoc} */
- public boolean tableExists(final byte [] tableName)
- throws MasterNotRunningException {
- getMaster();
- if (tableName == null) {
- throw new IllegalArgumentException("Table name cannot be null");
- }
- if (isMetaTableName(tableName)) {
- return true;
- }
- boolean exists = false;
- try {
- HTableDescriptor[] tables = listTables();
- for (int i = 0; i < tables.length; i++) {
- if (Bytes.equals(tables[i].getName(), tableName)) {
- exists = true;
- }
- }
- } catch (IOException e) {
- LOG.warn("Testing for table existence threw exception", e);
- }
- return exists;
- }
-
- /*
- * @param n
- * @return Truen if passed tablename <code>n</code> is equal to the name
- * of a catalog table.
- */
- private static boolean isMetaTableName(final byte [] n) {
- return Bytes.equals(n, ROOT_TABLE_NAME) ||
- Bytes.equals(n, META_TABLE_NAME);
- }
-
- /** {@inheritDoc} */
- public HRegionLocation getRegionLocation(final byte [] name,
- final byte [] row, boolean reload)
- throws IOException {
- getMaster();
- return reload? relocateRegion(name, row): locateRegion(name, row);
- }
-
- /** {@inheritDoc} */
- public HTableDescriptor[] listTables() throws IOException {
- getMaster();
- final HashSet<HTableDescriptor> uniqueTables =
- new HashSet<HTableDescriptor>();
-
- MetaScannerVisitor visitor = new MetaScannerVisitor() {
-
- /** {@inheritDoc} */
- public boolean processRow(RowResult rowResult) throws IOException {
- HRegionInfo info = Writables.getHRegionInfo(
- rowResult.get(COL_REGIONINFO));
-
- // Only examine the rows where the startKey is zero length
- if (info.getStartKey().length == 0) {
- uniqueTables.add(info.getTableDesc());
- }
- return true;
- }
-
- };
- MetaScanner.metaScan(conf, visitor);
-
- return uniqueTables.toArray(new HTableDescriptor[uniqueTables.size()]);
- }
-
- /** {@inheritDoc} */
- public boolean isTableEnabled(byte[] tableName) throws IOException {
- if (!tableExists(tableName)) {
- throw new TableNotFoundException(Bytes.toString(tableName));
- }
- if (Bytes.equals(tableName, HConstants.ROOT_TABLE_NAME)) {
- // The root region is always enabled
- return true;
- }
-
- boolean result = true;
- int rowsScanned = 0;
- byte[] startKey =
- HRegionInfo.createRegionName(tableName, null, HConstants.ZEROES);
- HRegionInfo currentRegion = null;
- do {
- if (currentRegion != null) {
- byte[] endKey = currentRegion.getEndKey();
- if (endKey == null ||
- Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY)) {
- // We have reached the end of the table and we're done
- break;
- }
- }
- HRegionInfo oldRegion = currentRegion;
- if (oldRegion != null) {
- startKey = oldRegion.getEndKey();
- }
- ScannerCallable s = new ScannerCallable(this,
- (Bytes.equals(tableName, HConstants.META_TABLE_NAME) ?
- HConstants.ROOT_TABLE_NAME : HConstants.META_TABLE_NAME),
- HConstants.COL_REGIONINFO_ARRAY, startKey,
- HConstants.LATEST_TIMESTAMP, null
- );
- // Open scanner
- getRegionServerWithRetries(s);
- currentRegion = s.getHRegionInfo();
- try {
- RowResult r = null;
- while (result && (r = getRegionServerWithRetries(s)) != null) {
- Cell c = r.get(HConstants.COL_REGIONINFO);
- if (c != null) {
- byte[] value = c.getValue();
- if (value != null) {
- HRegionInfo info = Writables.getHRegionInfoOrNull(value);
- if (info != null) {
- if (Bytes.equals(info.getTableDesc().getName(), tableName)) {
- rowsScanned += 1;
- result = !info.isOffline();
- }
- }
- }
- }
- }
- } finally {
- s.setClose();
- getRegionServerWithRetries(s);
- }
- } while (result);
- return rowsScanned > 0 && result;
- }
-
- /** {@inheritDoc} */
- public HTableDescriptor getHTableDescriptor(byte[] tableName)
- throws IOException {
- if (Bytes.equals(tableName, HConstants.ROOT_TABLE_NAME)) {
- return new UnmodifyableHTableDescriptor(HTableDescriptor.ROOT_TABLEDESC);
- }
- if (!tableExists(tableName)) {
- throw new TableNotFoundException(Bytes.toString(tableName));
- }
- byte[] startKey =
- HRegionInfo.createRegionName(tableName, null, HConstants.ZEROES);
-
- HTableDescriptor result = null;
- HRegionInfo currentRegion = null;
- ScannerCallable s = null;
- while (result == null) {
- if (currentRegion != null) {
- byte[] endKey = currentRegion.getEndKey();
- if (endKey == null ||
- Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY)) {
- // We have reached the end of the table and we're done
- break;
- }
- }
- HRegionInfo oldRegion = currentRegion;
- if (oldRegion != null) {
- startKey = oldRegion.getEndKey();
- }
- s = new ScannerCallable(this,
- (Bytes.equals(tableName, HConstants.META_TABLE_NAME) ?
- HConstants.ROOT_TABLE_NAME : HConstants.META_TABLE_NAME),
- HConstants.COL_REGIONINFO_ARRAY, startKey,
- HConstants.LATEST_TIMESTAMP, null
- );
- // Open scanner
- getRegionServerWithRetries(s);
- currentRegion = s.getHRegionInfo();
- try {
- RowResult r = null;
- while ((r = getRegionServerWithRetries(s)) != null) {
- Cell c = r.get(HConstants.COL_REGIONINFO);
- if (c != null) {
- HRegionInfo info = Writables.getHRegionInfoOrNull(c.getValue());
- if (info != null) {
- if (Bytes.equals(info.getTableDesc().getName(), tableName)) {
- result = new UnmodifyableHTableDescriptor(info.getTableDesc());
- break;
- }
- }
- }
- }
- } finally {
- s.setClose();
- getRegionServerWithRetries(s);
- }
- }
- return result;
- }
-
- /** {@inheritDoc} */
- public HRegionLocation locateRegion(final byte [] tableName,
- final byte [] row)
- throws IOException{
- getMaster();
- return locateRegion(tableName, row, true);
- }
-
- /** {@inheritDoc} */
- public HRegionLocation relocateRegion(final byte [] tableName,
- final byte [] row)
- throws IOException{
- getMaster();
- return locateRegion(tableName, row, false);
- }
-
- private HRegionLocation locateRegion(final byte [] tableName,
- final byte [] row, boolean useCache)
- throws IOException{
- if (tableName == null || tableName.length == 0) {
- throw new IllegalArgumentException(
- "table name cannot be null or zero length");
- }
-
- if (Bytes.equals(tableName, ROOT_TABLE_NAME)) {
- synchronized (rootRegionLock) {
- // This block guards against two threads trying to find the root
- // region at the same time. One will go do the find while the
- // second waits. The second thread will not do find.
-
- if (!useCache || rootRegionLocation == null) {
- return locateRootRegion();
- }
- return rootRegionLocation;
- }
- } else if (Bytes.equals(tableName, META_TABLE_NAME)) {
- synchronized (metaRegionLock) {
- // This block guards against two threads trying to load the meta
- // region at the same time. The first will load the meta region and
- // the second will use the value that the first one found.
-
- return locateRegionInMeta(ROOT_TABLE_NAME, tableName, row, useCache);
- }
- } else {
- synchronized(userRegionLock){
- return locateRegionInMeta(META_TABLE_NAME, tableName, row, useCache);
- }
- }
- }
-
- /**
- * Search one of the meta tables (-ROOT- or .META.) for the HRegionLocation
- * info that contains the table and row we're seeking.
- */
- private HRegionLocation locateRegionInMeta(final byte [] parentTable,
- final byte [] tableName, final byte [] row, boolean useCache)
- throws IOException{
- HRegionLocation location = null;
-
- // if we're supposed to be using the cache, then check it for a possible
- // hit. otherwise, delete any existing cached location so it won't
- // interfere.
- if (useCache) {
- location = getCachedLocation(tableName, row);
- if (location != null) {
- return location;
- }
- } else {
- deleteCachedLocation(tableName, row);
- }
-
- // build the key of the meta region we should be looking for.
- // the extra 9's on the end are necessary to allow "exact" matches
- // without knowing the precise region names.
- byte [] metaKey = HRegionInfo.createRegionName(tableName, row,
- HConstants.NINES);
- for (int tries = 0; true; tries++) {
- if (tries >= numRetries) {
- throw new NoServerForRegionException("Unable to find region for "
- + Bytes.toString(row) + " after " + numRetries + " tries.");
- }
-
- try{
- // locate the root region
- HRegionLocation metaLocation = locateRegion(parentTable, metaKey);
- HRegionInterface server =
- getHRegionConnection(metaLocation.getServerAddress());
-
- // query the root region for the location of the meta region
- RowResult regionInfoRow = server.getClosestRowBefore(
- metaLocation.getRegionInfo().getRegionName(), metaKey);
-
- if (regionInfoRow == null) {
- throw new TableNotFoundException(Bytes.toString(tableName));
- }
-
- Cell value = regionInfoRow.get(COL_REGIONINFO);
-
- if (value == null || value.getValue().length == 0) {
- throw new IOException("HRegionInfo was null or empty in " +
- Bytes.toString(parentTable));
- }
-
- // convert the row result into the HRegionLocation we need!
- HRegionInfo regionInfo = (HRegionInfo) Writables.getWritable(
- value.getValue(), new HRegionInfo());
-
- // possible we got a region of a different table...
- if (!Bytes.equals(regionInfo.getTableDesc().getName(), tableName)) {
- throw new TableNotFoundException(
- "Table '" + Bytes.toString(tableName) + "' was not found.");
- }
-
- if (regionInfo.isOffline()) {
- throw new RegionOfflineException("region offline: " +
- regionInfo.getRegionNameAsString());
- }
-
- String serverAddress =
- Writables.cellToString(regionInfoRow.get(COL_SERVER));
-
- if (serverAddress.equals("")) {
- throw new NoServerForRegionException("No server address listed " +
- "in " + Bytes.toString(parentTable) + " for region " +
- regionInfo.getRegionNameAsString());
- }
-
- // instantiate the location
- location = new HRegionLocation(regionInfo,
- new HServerAddress(serverAddress));
-
- cacheLocation(tableName, location);
-
- return location;
- } catch (TableNotFoundException e) {
- // if we got this error, probably means the table just plain doesn't
- // exist. rethrow the error immediately. this should always be coming
- // from the HTable constructor.
- throw e;
- } catch (IOException e) {
- if (e instanceof RemoteException) {
- e = RemoteExceptionHandler.decodeRemoteException(
- (RemoteException) e);
- }
- if (tries < numRetries - 1) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("reloading table servers because: " + e.getMessage());
- }
- relocateRegion(parentTable, metaKey);
- } else {
- throw e;
- }
- }
-
- try{
- Thread.sleep(pause);
- } catch (InterruptedException e){
- // continue
- }
- }
- }
-
- /*
- * Search the cache for a location that fits our table and row key.
- * Return null if no suitable region is located. TODO: synchronization note
- *
- * <p>TODO: This method during writing consumes 15% of CPU doing lookup
- * into the Soft Reference SortedMap. Improve.
- *
- * @param tableName
- * @param row
- * @return Null or region location found in cache.
- */
- private HRegionLocation getCachedLocation(final byte [] tableName,
- final byte [] row) {
- // find the map of cached locations for this table
- Integer key = Bytes.mapKey(tableName);
- SoftSortedMap<byte [], HRegionLocation> tableLocations =
- cachedRegionLocations.get(key);
-
- // if tableLocations for this table isn't built yet, make one
- if (tableLocations == null) {
- tableLocations = new SoftSortedMap<byte [],
- HRegionLocation>(Bytes.BYTES_COMPARATOR);
- cachedRegionLocations.put(key, tableLocations);
- }
-
- // start to examine the cache. we can only do cache actions
- // if there's something in the cache for this table.
- if (tableLocations.isEmpty()) {
- return null;
- }
-
- HRegionLocation rl = tableLocations.get(row);
- if (rl != null) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("Cache hit in table locations for row <" +
- Bytes.toString(row) +
- "> and tableName " + Bytes.toString(tableName) +
- ": location server " + rl.getServerAddress() +
- ", location region name " +
- rl.getRegionInfo().getRegionNameAsString());
- }
- return rl;
- }
-
- // Cut the cache so that we only get the part that could contain
- // regions that match our key
- SoftSortedMap<byte[], HRegionLocation> matchingRegions =
- tableLocations.headMap(row);
-
- // if that portion of the map is empty, then we're done. otherwise,
- // we need to examine the cached location to verify that it is
- // a match by end key as well.
- if (!matchingRegions.isEmpty()) {
- HRegionLocation possibleRegion =
- matchingRegions.get(matchingRegions.lastKey());
-
- // there is a possibility that the reference was garbage collected
- // in the instant since we checked isEmpty().
- if (possibleRegion != null) {
- byte[] endKey = possibleRegion.getRegionInfo().getEndKey();
-
- // make sure that the end key is greater than the row we're looking
- // for, otherwise the row actually belongs in the next region, not
- // this one. the exception case is when the endkey is EMPTY_START_ROW,
- // signifying that the region we're checking is actually the last
- // region in the table.
- if (Bytes.equals(endKey, HConstants.EMPTY_END_ROW) ||
- Bytes.compareTo(endKey, row) > 0) {
- return possibleRegion;
- }
- }
- }
-
- // Passed all the way through, so we got nothin - complete cache miss
- return null;
- }
-
- /**
- * Delete a cached location, if it satisfies the table name and row
- * requirements.
- */
- private void deleteCachedLocation(final byte [] tableName,
- final byte [] row) {
- // find the map of cached locations for this table
- Integer key = Bytes.mapKey(tableName);
- SoftSortedMap<byte [], HRegionLocation> tableLocations =
- cachedRegionLocations.get(key);
-
- // if tableLocations for this table isn't built yet, make one
- if (tableLocations == null) {
- tableLocations =
- new SoftSortedMap<byte [], HRegionLocation>(Bytes.BYTES_COMPARATOR);
- cachedRegionLocations.put(key, tableLocations);
- }
-
- // start to examine the cache. we can only do cache actions
- // if there's something in the cache for this table.
- if (!tableLocations.isEmpty()) {
- // cut the cache so that we only get the part that could contain
- // regions that match our key
- SoftSortedMap<byte [], HRegionLocation> matchingRegions =
- tableLocations.headMap(row);
-
- // if that portion of the map is empty, then we're done. otherwise,
- // we need to examine the cached location to verify that it is
- // a match by end key as well.
- if (!matchingRegions.isEmpty()) {
- HRegionLocation possibleRegion =
- matchingRegions.get(matchingRegions.lastKey());
-
- byte [] endKey = possibleRegion.getRegionInfo().getEndKey();
-
- // by nature of the map, we know that the start key has to be <
- // otherwise it wouldn't be in the headMap.
- if (Bytes.compareTo(endKey, row) <= 0) {
- // delete any matching entry
- HRegionLocation rl =
- tableLocations.remove(matchingRegions.lastKey());
- if (rl != null && LOG.isDebugEnabled()) {
- LOG.debug("Removed " + rl.getRegionInfo().getRegionNameAsString() +
- " from cache because of " + Bytes.toString(row));
- }
- }
- }
- }
- }
-
- /**
- * Put a newly discovered HRegionLocation into the cache.
- */
- private void cacheLocation(final byte [] tableName,
- final HRegionLocation location){
- byte [] startKey = location.getRegionInfo().getStartKey();
-
- // find the map of cached locations for this table
- Integer key = Bytes.mapKey(tableName);
- SoftSortedMap<byte [], HRegionLocation> tableLocations =
- cachedRegionLocations.get(key);
-
- // if tableLocations for this table isn't built yet, make one
- if (tableLocations == null) {
- tableLocations =
- new SoftSortedMap<byte [], HRegionLocation>(Bytes.BYTES_COMPARATOR);
- cachedRegionLocations.put(key, tableLocations);
- }
-
- // save the HRegionLocation under the startKey
- tableLocations.put(startKey, location);
- }
-
- /** {@inheritDoc} */
- public HRegionInterface getHRegionConnection(HServerAddress regionServer)
- throws IOException {
- getMaster();
- HRegionInterface server;
- synchronized (this.servers) {
- // See if we already have a connection
- server = this.servers.get(regionServer.toString());
- if (server == null) { // Get a connection
- long versionId = 0;
- try {
- versionId =
- serverInterfaceClass.getDeclaredField("versionID").getLong(server);
- } catch (IllegalAccessException e) {
- // Should never happen unless visibility of versionID changes
- throw new UnsupportedOperationException(
- "Unable to open a connection to a " +
- serverInterfaceClass.getName() + " server.", e);
- } catch (NoSuchFieldException e) {
- // Should never happen unless versionID field name changes in HRegionInterface
- throw new UnsupportedOperationException(
- "Unable to open a connection to a " +
- serverInterfaceClass.getName() + " server.", e);
- }
-
- try {
- server = (HRegionInterface)HbaseRPC.waitForProxy(serverInterfaceClass,
- versionId, regionServer.getInetSocketAddress(), this.conf,
- this.maxRPCAttempts);
- } catch (RemoteException e) {
- throw RemoteExceptionHandler.decodeRemoteException(e);
- }
- this.servers.put(regionServer.toString(), server);
- }
- }
- return server;
- }
-
- /*
- * Repeatedly try to find the root region by asking the master for where it is
- * @return HRegionLocation for root region if found
- * @throws NoServerForRegionException - if the root region can not be located
- * after retrying
- * @throws IOException
- */
- private HRegionLocation locateRootRegion()
- throws IOException {
- getMaster();
- HServerAddress rootRegionAddress = null;
- for (int tries = 0; tries < numRetries; tries++) {
- int localTimeouts = 0;
-
- // ask the master which server has the root region
- while (rootRegionAddress == null && localTimeouts < numRetries) {
- rootRegionAddress = master.findRootRegion();
- if (rootRegionAddress == null) {
- try {
- if (LOG.isDebugEnabled()) {
- LOG.debug("Sleeping. Waiting for root region.");
- }
- Thread.sleep(pause);
- if (LOG.isDebugEnabled()) {
- LOG.debug("Wake. Retry finding root region.");
- }
- } catch (InterruptedException iex) {
- // continue
- }
- localTimeouts++;
- }
- }
-
- if (rootRegionAddress == null) {
- throw new NoServerForRegionException(
- "Timed out trying to locate root region");
- }
-
- // get a connection to the region server
- HRegionInterface server = getHRegionConnection(rootRegionAddress);
-
- try {
- // if this works, then we're good, and we have an acceptable address,
- // so we can stop doing retries and return the result.
- server.getRegionInfo(HRegionInfo.ROOT_REGIONINFO.getRegionName());
- if (LOG.isDebugEnabled()) {
- LOG.debug("Found ROOT " + HRegionInfo.ROOT_REGIONINFO);
- }
- break;
- } catch (IOException e) {
- if (tries == numRetries - 1) {
- // Don't bother sleeping. We've run out of retries.
- if (e instanceof RemoteException) {
- e = RemoteExceptionHandler.decodeRemoteException(
- (RemoteException) e);
- }
- throw e;
- }
-
- // Sleep and retry finding root region.
- try {
- if (LOG.isDebugEnabled()) {
- LOG.debug("Root region location changed. Sleeping.");
- }
- Thread.sleep(pause);
- if (LOG.isDebugEnabled()) {
- LOG.debug("Wake. Retry finding root region.");
- }
- } catch (InterruptedException iex) {
- // continue
- }
- }
-
- rootRegionAddress = null;
- }
-
- // if the address is null by this point, then the retries have failed,
- // and we're sort of sunk
- if (rootRegionAddress == null) {
- throw new NoServerForRegionException(
- "unable to locate root region server");
- }
-
- // return the region location
- return new HRegionLocation(
- HRegionInfo.ROOT_REGIONINFO, rootRegionAddress);
- }
-
- /** {@inheritDoc} */
- public <T> T getRegionServerWithRetries(ServerCallable<T> callable)
- throws IOException, RuntimeException {
- getMaster();
- List<Throwable> exceptions = new ArrayList<Throwable>();
- for(int tries = 0; tries < numRetries; tries++) {
- try {
- callable.instantiateServer(tries != 0);
- return callable.call();
- } catch (Throwable t) {
- if (t instanceof UndeclaredThrowableException) {
- t = t.getCause();
- }
- if (t instanceof RemoteException) {
- t = RemoteExceptionHandler.decodeRemoteException((RemoteException) t);
- }
- if (t instanceof DoNotRetryIOException) {
- throw (DoNotRetryIOException)t;
- }
- exceptions.add(t);
- if (tries == numRetries - 1) {
- throw new RetriesExhaustedException(callable.getServerName(),
- callable.getRegionName(), callable.getRow(), tries, exceptions);
- }
- if (LOG.isDebugEnabled()) {
- LOG.debug("reloading table servers because: " + t.getMessage());
- }
- }
- try {
- Thread.sleep(pause);
- } catch (InterruptedException e) {
- // continue
- }
- }
- return null;
- }
- }
-}
+/**
+ * Copyright 2007 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.lang.reflect.UndeclaredThrowableException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.DoNotRetryIOException;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.HRegionLocation;
+import org.apache.hadoop.hbase.HServerAddress;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.LocalHBaseCluster;
+import org.apache.hadoop.hbase.MasterNotRunningException;
+import org.apache.hadoop.hbase.RemoteExceptionHandler;
+import org.apache.hadoop.hbase.TableNotFoundException;
+import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor;
+import org.apache.hadoop.hbase.io.Cell;
+import org.apache.hadoop.hbase.io.RowResult;
+import org.apache.hadoop.hbase.ipc.HMasterInterface;
+import org.apache.hadoop.hbase.ipc.HRegionInterface;
+import org.apache.hadoop.hbase.ipc.HbaseRPC;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.SoftSortedMap;
+import org.apache.hadoop.hbase.util.Writables;
+import org.apache.hadoop.ipc.RemoteException;
+
+/**
+ * A non-instantiable class that manages connections to multiple tables in
+ * multiple HBase instances.
+ *
+ * Used by {@link HTable} and {@link HBaseAdmin}
+ */
+public class HConnectionManager implements HConstants {
+ /*
+ * Private. Not instantiable.
+ */
+ private HConnectionManager() {
+ super();
+ }
+
+ // A Map of master HServerAddress -> connection information for that instance
+ // Note that although the Map is synchronized, the objects it contains
+ // are mutable and hence require synchronized access to them
+ private static final Map<String, TableServers> HBASE_INSTANCES =
+ new ConcurrentHashMap<String, TableServers>();
+
+ /**
+ * Get the connection object for the instance specified by the configuration
+ * If no current connection exists, create a new connection for that instance
+ * @param conf
+ * @return HConnection object for the instance specified by the configuration
+ */
+ public static HConnection getConnection(HBaseConfiguration conf) {
+ TableServers connection;
+ synchronized (HBASE_INSTANCES) {
+ String instanceName = conf.get(HBASE_DIR);
+ connection = HBASE_INSTANCES.get(instanceName);
+ if (connection == null) {
+ connection = new TableServers(conf);
+ HBASE_INSTANCES.put(instanceName, connection);
+ }
+ }
+ return connection;
+ }
+
+ /**
+ * Delete connection information for the instance specified by the configuration
+ * @param conf
+ */
+ public static void deleteConnectionInfo(HBaseConfiguration conf) {
+ synchronized (HBASE_INSTANCES) {
+ HBASE_INSTANCES.remove(conf.get(HBASE_DIR));
+ }
+ }
+
+ /**
+ * Clear the static map of connection info.
+ */
+ public static void deleteConnectionInfo() {
+ synchronized (HBASE_INSTANCES) {
+ HBASE_INSTANCES.clear();
+ }
+ }
+
+
+ /* Encapsulates finding the servers for an HBase instance */
+ private static class TableServers implements HConnection, HConstants {
+ private static final Log LOG = LogFactory.getLog(TableServers.class);
+ private final Class<? extends HRegionInterface> serverInterfaceClass;
+ private final long pause;
+ private final int numRetries;
+ private final int maxRPCAttempts;
+
+ private final Integer masterLock = new Integer(0);
+ private volatile boolean closed;
+ private volatile HMasterInterface master;
+ private volatile boolean masterChecked;
+
+ private final Integer rootRegionLock = new Integer(0);
+ private final Integer metaRegionLock = new Integer(0);
+ private final Integer userRegionLock = new Integer(0);
+
+ private volatile HBaseConfiguration conf;
+
+ // Known region HServerAddress.toString() -> HRegionInterface
+ private final Map<String, HRegionInterface> servers =
+ new ConcurrentHashMap<String, HRegionInterface>();
+
+ private HRegionLocation rootRegionLocation;
+
+ private final Map<Integer, SoftSortedMap<byte [], HRegionLocation>>
+ cachedRegionLocations = Collections.synchronizedMap(
+ new HashMap<Integer, SoftSortedMap<byte [], HRegionLocation>>());
+
+ /**
+ * constructor
+ * @param conf Configuration object
+ */
+ @SuppressWarnings("unchecked")
+ public TableServers(HBaseConfiguration conf) {
+ this.conf = LocalHBaseCluster.doLocal(new HBaseConfiguration(conf));
+
+ String serverClassName =
+ conf.get(REGION_SERVER_CLASS, DEFAULT_REGION_SERVER_CLASS);
+
+ this.closed = false;
+
+ try {
+ this.serverInterfaceClass =
+ (Class<? extends HRegionInterface>) Class.forName(serverClassName);
+
+ } catch (ClassNotFoundException e) {
+ throw new UnsupportedOperationException(
+ "Unable to find region server interface " + serverClassName, e);
+ }
+
+ this.pause = conf.getLong("hbase.client.pause", 10 * 1000);
+ this.numRetries = conf.getInt("hbase.client.retries.number", 10);
+ this.maxRPCAttempts = conf.getInt("hbase.client.rpc.maxattempts", 1);
+
+ this.master = null;
+ this.masterChecked = false;
+ }
+
+ private long getPauseTime(int tries) {
+ if (tries >= HConstants.RETRY_BACKOFF.length)
+ tries = HConstants.RETRY_BACKOFF.length - 1;
+ return this.pause * HConstants.RETRY_BACKOFF[tries];
+ }
+
+ /** {@inheritDoc} */
+ public HMasterInterface getMaster() throws MasterNotRunningException {
+ HServerAddress masterLocation = null;
+ synchronized (this.masterLock) {
+ for (int tries = 0;
+ !this.closed &&
+ !this.masterChecked && this.master == null &&
+ tries < numRetries;
+ tries++) {
+
+ masterLocation = new HServerAddress(this.conf.get(MASTER_ADDRESS,
+ DEFAULT_MASTER_ADDRESS));
+ try {
+ HMasterInterface tryMaster = (HMasterInterface)HbaseRPC.getProxy(
+ HMasterInterface.class, HMasterInterface.versionID,
+ masterLocation.getInetSocketAddress(), this.conf);
+
+ if (tryMaster.isMasterRunning()) {
+ this.master = tryMaster;
+ break;
+ }
+
+ } catch (IOException e) {
+ if(tries == numRetries - 1) {
+ // This was our last chance - don't bother sleeping
+ break;
+ }
+ LOG.info("Attempt " + tries + " of " + this.numRetries +
+ " failed with <" + e + ">. Retrying after sleep of " +
+ getPauseTime(tries));
+ }
+
+ // We either cannot connect to master or it is not running. Sleep & retry
+
+ try {
+ Thread.sleep(getPauseTime(tries));
+ } catch (InterruptedException e) {
+ // continue
+ }
+ }
+ this.masterChecked = true;
+ }
+ if (this.master == null) {
+ if (masterLocation == null) {
+ throw new MasterNotRunningException();
+ }
+ throw new MasterNotRunningException(masterLocation.toString());
+ }
+ return this.master;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isMasterRunning() {
+ if (this.master == null) {
+ try {
+ getMaster();
+
+ } catch (MasterNotRunningException e) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public boolean tableExists(final byte [] tableName)
+ throws MasterNotRunningException {
+ getMaster();
+ if (tableName == null) {
+ throw new IllegalArgumentException("Table name cannot be null");
+ }
+ if (isMetaTableName(tableName)) {
+ return true;
+ }
+ boolean exists = false;
+ try {
+ HTableDescriptor[] tables = listTables();
+ for (int i = 0; i < tables.length; i++) {
+ if (Bytes.equals(tables[i].getName(), tableName)) {
+ exists = true;
+ }
+ }
+ } catch (IOException e) {
+ LOG.warn("Testing for table existence threw exception", e);
+ }
+ return exists;
+ }
+
+ /*
+ * @param n
+ * @return Truen if passed tablename <code>n</code> is equal to the name
+ * of a catalog table.
+ */
+ private static boolean isMetaTableName(final byte [] n) {
+ return Bytes.equals(n, ROOT_TABLE_NAME) ||
+ Bytes.equals(n, META_TABLE_NAME);
+ }
+
+ /** {@inheritDoc} */
+ public HRegionLocation getRegionLocation(final byte [] name,
+ final byte [] row, boolean reload)
+ throws IOException {
+ getMaster();
+ return reload? relocateRegion(name, row): locateRegion(name, row);
+ }
+
+ /** {@inheritDoc} */
+ public HTableDescriptor[] listTables() throws IOException {
+ getMaster();
+ final HashSet<HTableDescriptor> uniqueTables =
+ new HashSet<HTableDescriptor>();
+
+ MetaScannerVisitor visitor = new MetaScannerVisitor() {
+
+ /** {@inheritDoc} */
+ public boolean processRow(RowResult rowResult) throws IOException {
+ HRegionInfo info = Writables.getHRegionInfo(
+ rowResult.get(COL_REGIONINFO));
+
+ // Only examine the rows where the startKey is zero length
+ if (info.getStartKey().length == 0) {
+ uniqueTables.add(info.getTableDesc());
+ }
+ return true;
+ }
+
+ };
+ MetaScanner.metaScan(conf, visitor);
+
+ return uniqueTables.toArray(new HTableDescriptor[uniqueTables.size()]);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isTableEnabled(byte[] tableName) throws IOException {
+ if (!tableExists(tableName)) {
+ throw new TableNotFoundException(Bytes.toString(tableName));
+ }
+ if (Bytes.equals(tableName, HConstants.ROOT_TABLE_NAME)) {
+ // The root region is always enabled
+ return true;
+ }
+
+ boolean result = true;
+ int rowsScanned = 0;
+ byte[] startKey =
+ HRegionInfo.createRegionName(tableName, null, HConstants.ZEROES);
+ HRegionInfo currentRegion = null;
+ do {
+ if (currentRegion != null) {
+ byte[] endKey = currentRegion.getEndKey();
+ if (endKey == null ||
+ Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY)) {
+ // We have reached the end of the table and we're done
+ break;
+ }
+ }
+ HRegionInfo oldRegion = currentRegion;
+ if (oldRegion != null) {
+ startKey = oldRegion.getEndKey();
+ }
+ ScannerCallable s = new ScannerCallable(this,
+ (Bytes.equals(tableName, HConstants.META_TABLE_NAME) ?
+ HConstants.ROOT_TABLE_NAME : HConstants.META_TABLE_NAME),
+ HConstants.COL_REGIONINFO_ARRAY, startKey,
+ HConstants.LATEST_TIMESTAMP, null
+ );
+ // Open scanner
+ getRegionServerWithRetries(s);
+ currentRegion = s.getHRegionInfo();
+ try {
+ RowResult r = null;
+ while (result && (r = getRegionServerWithRetries(s)) != null) {
+ Cell c = r.get(HConstants.COL_REGIONINFO);
+ if (c != null) {
+ byte[] value = c.getValue();
+ if (value != null) {
+ HRegionInfo info = Writables.getHRegionInfoOrNull(value);
+ if (info != null) {
+ if (Bytes.equals(info.getTableDesc().getName(), tableName)) {
+ rowsScanned += 1;
+ result = !info.isOffline();
+ }
+ }
+ }
+ }
+ }
+ } finally {
+ s.setClose();
+ getRegionServerWithRetries(s);
+ }
+ } while (result);
+ return rowsScanned > 0 && result;
+ }
+
+ /** {@inheritDoc} */
+ public HTableDescriptor getHTableDescriptor(byte[] tableName)
+ throws IOException {
+ if (Bytes.equals(tableName, HConstants.ROOT_TABLE_NAME)) {
+ return new UnmodifyableHTableDescriptor(HTableDescriptor.ROOT_TABLEDESC);
+ }
+ if (!tableExists(tableName)) {
+ throw new TableNotFoundException(Bytes.toString(tableName));
+ }
+ byte[] startKey =
+ HRegionInfo.createRegionName(tableName, null, HConstants.ZEROES);
+
+ HTableDescriptor result = null;
+ HRegionInfo currentRegion = null;
+ ScannerCallable s = null;
+ while (result == null) {
+ if (currentRegion != null) {
+ byte[] endKey = currentRegion.getEndKey();
+ if (endKey == null ||
+ Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY)) {
+ // We have reached the end of the table and we're done
+ break;
+ }
+ }
+ HRegionInfo oldRegion = currentRegion;
+ if (oldRegion != null) {
+ startKey = oldRegion.getEndKey();
+ }
+ s = new ScannerCallable(this,
+ (Bytes.equals(tableName, HConstants.META_TABLE_NAME) ?
+ HConstants.ROOT_TABLE_NAME : HConstants.META_TABLE_NAME),
+ HConstants.COL_REGIONINFO_ARRAY, startKey,
+ HConstants.LATEST_TIMESTAMP, null
+ );
+ // Open scanner
+ getRegionServerWithRetries(s);
+ currentRegion = s.getHRegionInfo();
+ try {
+ RowResult r = null;
+ while ((r = getRegionServerWithRetries(s)) != null) {
+ Cell c = r.get(HConstants.COL_REGIONINFO);
+ if (c != null) {
+ HRegionInfo info = Writables.getHRegionInfoOrNull(c.getValue());
+ if (info != null) {
+ if (Bytes.equals(info.getTableDesc().getName(), tableName)) {
+ result = new UnmodifyableHTableDescriptor(info.getTableDesc());
+ break;
+ }
+ }
+ }
+ }
+ } finally {
+ s.setClose();
+ getRegionServerWithRetries(s);
+ }
+ }
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public HRegionLocation locateRegion(final byte [] tableName,
+ final byte [] row)
+ throws IOException{
+ getMaster();
+ return locateRegion(tableName, row, true);
+ }
+
+ /** {@inheritDoc} */
+ public HRegionLocation relocateRegion(final byte [] tableName,
+ final byte [] row)
+ throws IOException{
+ getMaster();
+ return locateRegion(tableName, row, false);
+ }
+
+ private HRegionLocation locateRegion(final byte [] tableName,
+ final byte [] row, boolean useCache)
+ throws IOException{
+ if (tableName == null || tableName.length == 0) {
+ throw new IllegalArgumentException(
+ "table name cannot be null or zero length");
+ }
+
+ if (Bytes.equals(tableName, ROOT_TABLE_NAME)) {
+ synchronized (rootRegionLock) {
+ // This block guards against two threads trying to find the root
+ // region at the same time. One will go do the find while the
+ // second waits. The second thread will not do find.
+
+ if (!useCache || rootRegionLocation == null) {
+ return locateRootRegion();
+ }
+ return rootRegionLocation;
+ }
+ } else if (Bytes.equals(tableName, META_TABLE_NAME)) {
+ synchronized (metaRegionLock) {
+ // This block guards against two threads trying to load the meta
+ // region at the same time. The first will load the meta region and
+ // the second will use the value that the first one found.
+
+ return locateRegionInMeta(ROOT_TABLE_NAME, tableName, row, useCache);
+ }
+ } else {
+ synchronized(userRegionLock){
+ return locateRegionInMeta(META_TABLE_NAME, tableName, row, useCache);
+ }
+ }
+ }
+
+ /**
+ * Search one of the meta tables (-ROOT- or .META.) for the HRegionLocation
+ * info that contains the table and row we're seeking.
+ */
+ private HRegionLocation locateRegionInMeta(final byte [] parentTable,
+ final byte [] tableName, final byte [] row, boolean useCache)
+ throws IOException{
+ HRegionLocation location = null;
+
+ // if we're supposed to be using the cache, then check it for a possible
+ // hit. otherwise, delete any existing cached location so it won't
+ // interfere.
+ if (useCache) {
+ location = getCachedLocation(tableName, row);
+ if (location != null) {
+ return location;
+ }
+ } else {
+ deleteCachedLocation(tableName, row);
+ }
+
+ // build the key of the meta region we should be looking for.
+ // the extra 9's on the end are necessary to allow "exact" matches
+ // without knowing the precise region names.
+ byte [] metaKey = HRegionInfo.createRegionName(tableName, row,
+ HConstants.NINES);
+ for (int tries = 0; true; tries++) {
+ if (tries >= numRetries) {
+ throw new NoServerForRegionException("Unable to find region for "
+ + Bytes.toString(row) + " after " + numRetries + " tries.");
+ }
+
+ try{
+ // locate the root region
+ HRegionLocation metaLocation = locateRegion(parentTable, metaKey);
+ HRegionInterface server =
+ getHRegionConnection(metaLocation.getServerAddress());
+
+ // query the root region for the location of the meta region
+ RowResult regionInfoRow = server.getClosestRowBefore(
+ metaLocation.getRegionInfo().getRegionName(), metaKey);
+
+ if (regionInfoRow == null) {
+ throw new TableNotFoundException(Bytes.toString(tableName));
+ }
+
+ Cell value = regionInfoRow.get(COL_REGIONINFO);
+
+ if (value == null || value.getValue().length == 0) {
+ throw new IOException("HRegionInfo was null or empty in " +
+ Bytes.toString(parentTable));
+ }
+
+ // convert the row result into the HRegionLocation we need!
+ HRegionInfo regionInfo = (HRegionInfo) Writables.getWritable(
+ value.getValue(), new HRegionInfo());
+
+ // possible we got a region of a different table...
+ if (!Bytes.equals(regionInfo.getTableDesc().getName(), tableName)) {
+ throw new TableNotFoundException(
+ "Table '" + Bytes.toString(tableName) + "' was not found.");
+ }
+
+ if (regionInfo.isOffline()) {
+ throw new RegionOfflineException("region offline: " +
+ regionInfo.getRegionNameAsString());
+ }
+
+ String serverAddress =
+ Writables.cellToString(regionInfoRow.get(COL_SERVER));
+
+ if (serverAddress.equals("")) {
+ throw new NoServerForRegionException("No server address listed " +
+ "in " + Bytes.toString(parentTable) + " for region " +
+ regionInfo.getRegionNameAsString());
+ }
+
+ // instantiate the location
+ location = new HRegionLocation(regionInfo,
+ new HServerAddress(serverAddress));
+
+ cacheLocation(tableName, location);
+
+ return location;
+ } catch (TableNotFoundException e) {
+ // if we got this error, probably means the table just plain doesn't
+ // exist. rethrow the error immediately. this should always be coming
+ // from the HTable constructor.
+ throw e;
+ } catch (IOException e) {
+ if (e instanceof RemoteException) {
+ e = RemoteExceptionHandler.decodeRemoteException(
+ (RemoteException) e);
+ }
+ if (tries < numRetries - 1) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("reloading table servers because: " + e.getMessage());
+ }
+ relocateRegion(parentTable, metaKey);
+ } else {
+ throw e;
+ }
+ }
+
+ try{
+ Thread.sleep(getPauseTime(tries));
+ } catch (InterruptedException e){
+ // continue
+ }
+ }
+ }
+
+ /*
+ * Search the cache for a location that fits our table and row key.
+ * Return null if no suitable region is located. TODO: synchronization note
+ *
+ * <p>TODO: This method during writing consumes 15% of CPU doing lookup
+ * into the Soft Reference SortedMap. Improve.
+ *
+ * @param tableName
+ * @param row
+ * @return Null or region location found in cache.
+ */
+ private HRegionLocation getCachedLocation(final byte [] tableName,
+ final byte [] row) {
+ // find the map of cached locations for this table
+ Integer key = Bytes.mapKey(tableName);
+ SoftSortedMap<byte [], HRegionLocation> tableLocations =
+ cachedRegionLocations.get(key);
+
+ // if tableLocations for this table isn't built yet, make one
+ if (tableLocations == null) {
+ tableLocations = new SoftSortedMap<byte [],
+ HRegionLocation>(Bytes.BYTES_COMPARATOR);
+ cachedRegionLocations.put(key, tableLocations);
+ }
+
+ // start to examine the cache. we can only do cache actions
+ // if there's something in the cache for this table.
+ if (tableLocations.isEmpty()) {
+ return null;
+ }
+
+ HRegionLocation rl = tableLocations.get(row);
+ if (rl != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Cache hit in table locations for row <" +
+ Bytes.toString(row) +
+ "> and tableName " + Bytes.toString(tableName) +
+ ": location server " + rl.getServerAddress() +
+ ", location region name " +
+ rl.getRegionInfo().getRegionNameAsString());
+ }
+ return rl;
+ }
+
+ // Cut the cache so that we only get the part that could contain
+ // regions that match our key
+ SoftSortedMap<byte[], HRegionLocation> matchingRegions =
+ tableLocations.headMap(row);
+
+ // if that portion of the map is empty, then we're done. otherwise,
+ // we need to examine the cached location to verify that it is
+ // a match by end key as well.
+ if (!matchingRegions.isEmpty()) {
+ HRegionLocation possibleRegion =
+ matchingRegions.get(matchingRegions.lastKey());
+
+ // there is a possibility that the reference was garbage collected
+ // in the instant since we checked isEmpty().
+ if (possibleRegion != null) {
+ byte[] endKey = possibleRegion.getRegionInfo().getEndKey();
+
+ // make sure that the end key is greater than the row we're looking
+ // for, otherwise the row actually belongs in the next region, not
+ // this one. the exception case is when the endkey is EMPTY_START_ROW,
+ // signifying that the region we're checking is actually the last
+ // region in the table.
+ if (Bytes.equals(endKey, HConstants.EMPTY_END_ROW) ||
+ Bytes.compareTo(endKey, row) > 0) {
+ return possibleRegion;
+ }
+ }
+ }
+
+ // Passed all the way through, so we got nothin - complete cache miss
+ return null;
+ }
+
+ /**
+ * Delete a cached location, if it satisfies the table name and row
+ * requirements.
+ */
+ private void deleteCachedLocation(final byte [] tableName,
+ final byte [] row) {
+ // find the map of cached locations for this table
+ Integer key = Bytes.mapKey(tableName);
+ SoftSortedMap<byte [], HRegionLocation> tableLocations =
+ cachedRegionLocations.get(key);
+
+ // if tableLocations for this table isn't built yet, make one
+ if (tableLocations == null) {
+ tableLocations =
+ new SoftSortedMap<byte [], HRegionLocation>(Bytes.BYTES_COMPARATOR);
+ cachedRegionLocations.put(key, tableLocations);
+ }
+
+ // start to examine the cache. we can only do cache actions
+ // if there's something in the cache for this table.
+ if (!tableLocations.isEmpty()) {
+ // cut the cache so that we only get the part that could contain
+ // regions that match our key
+ SoftSortedMap<byte [], HRegionLocation> matchingRegions =
+ tableLocations.headMap(row);
+
+ // if that portion of the map is empty, then we're done. otherwise,
+ // we need to examine the cached location to verify that it is
+ // a match by end key as well.
+ if (!matchingRegions.isEmpty()) {
+ HRegionLocation possibleRegion =
+ matchingRegions.get(matchingRegions.lastKey());
+
+ byte [] endKey = possibleRegion.getRegionInfo().getEndKey();
+
+ // by nature of the map, we know that the start key has to be <
+ // otherwise it wouldn't be in the headMap.
+ if (Bytes.compareTo(endKey, row) <= 0) {
+ // delete any matching entry
+ HRegionLocation rl =
+ tableLocations.remove(matchingRegions.lastKey());
+ if (rl != null && LOG.isDebugEnabled()) {
+ LOG.debug("Removed " + rl.getRegionInfo().getRegionNameAsString() +
+ " from cache because of " + Bytes.toString(row));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Put a newly discovered HRegionLocation into the cache.
+ */
+ private void cacheLocation(final byte [] tableName,
+ final HRegionLocation location){
+ byte [] startKey = location.getRegionInfo().getStartKey();
+
+ // find the map of cached locations for this table
+ Integer key = Bytes.mapKey(tableName);
+ SoftSortedMap<byte [], HRegionLocation> tableLocations =
+ cachedRegionLocations.get(key);
+
+ // if tableLocations for this table isn't built yet, make one
+ if (tableLocations == null) {
+ tableLocations =
+ new SoftSortedMap<byte [], HRegionLocation>(Bytes.BYTES_COMPARATOR);
+ cachedRegionLocations.put(key, tableLocations);
+ }
+
+ // save the HRegionLocation under the startKey
+ tableLocations.put(startKey, location);
+ }
+
+ /** {@inheritDoc} */
+ public HRegionInterface getHRegionConnection(HServerAddress regionServer)
+ throws IOException {
+ getMaster();
+ HRegionInterface server;
+ synchronized (this.servers) {
+ // See if we already have a connection
+ server = this.servers.get(regionServer.toString());
+ if (server == null) { // Get a connection
+ long versionId = 0;
+ try {
+ versionId =
+ serverInterfaceClass.getDeclaredField("versionID").getLong(server);
+ } catch (IllegalAccessException e) {
+ // Should never happen unless visibility of versionID changes
+ throw new UnsupportedOperationException(
+ "Unable to open a connection to a " +
+ serverInterfaceClass.getName() + " server.", e);
+ } catch (NoSuchFieldException e) {
+ // Should never happen unless versionID field name changes in HRegionInterface
+ throw new UnsupportedOperationException(
+ "Unable to open a connection to a " +
+ serverInterfaceClass.getName() + " server.", e);
+ }
+
+ try {
+ server = (HRegionInterface)HbaseRPC.waitForProxy(serverInterfaceClass,
+ versionId, regionServer.getInetSocketAddress(), this.conf,
+ this.maxRPCAttempts);
+ } catch (RemoteException e) {
+ throw RemoteExceptionHandler.decodeRemoteException(e);
+ }
+ this.servers.put(regionServer.toString(), server);
+ }
+ }
+ return server;
+ }
+
+ /*
+ * Repeatedly try to find the root region by asking the master for where it is
+ * @return HRegionLocation for root region if found
+ * @throws NoServerForRegionException - if the root region can not be located
+ * after retrying
+ * @throws IOException
+ */
+ private HRegionLocation locateRootRegion()
+ throws IOException {
+ getMaster();
+ HServerAddress rootRegionAddress = null;
+ for (int tries = 0; tries < numRetries; tries++) {
+ int localTimeouts = 0;
+
+ // ask the master which server has the root region
+ while (rootRegionAddress == null && localTimeouts < numRetries) {
+ rootRegionAddress = master.findRootRegion();
+ if (rootRegionAddress == null) {
+ try {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Sleeping. Waiting for root region.");
+ }
+ Thread.sleep(getPauseTime(tries));
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Wake. Retry finding root region.");
+ }
+ } catch (InterruptedException iex) {
+ // continue
+ }
+ localTimeouts++;
+ }
+ }
+
+ if (rootRegionAddress == null) {
+ throw new NoServerForRegionException(
+ "Timed out trying to locate root region");
+ }
+
+ // get a connection to the region server
+ HRegionInterface server = getHRegionConnection(rootRegionAddress);
+
+ try {
+ // if this works, then we're good, and we have an acceptable address,
+ // so we can stop doing retries and return the result.
+ server.getRegionInfo(HRegionInfo.ROOT_REGIONINFO.getRegionName());
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Found ROOT " + HRegionInfo.ROOT_REGIONINFO);
+ }
+ break;
+ } catch (IOException e) {
+ if (tries == numRetries - 1) {
+ // Don't bother sleeping. We've run out of retries.
+ if (e instanceof RemoteException) {
+ e = RemoteExceptionHandler.decodeRemoteException(
+ (RemoteException) e);
+ }
+ throw e;
+ }
+
+ // Sleep and retry finding root region.
+ try {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Root region location changed. Sleeping.");
+ }
+ Thread.sleep(getPauseTime(tries));
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Wake. Retry finding root region.");
+ }
+ } catch (InterruptedException iex) {
+ // continue
+ }
+ }
+
+ rootRegionAddress = null;
+ }
+
+ // if the address is null by this point, then the retries have failed,
+ // and we're sort of sunk
+ if (rootRegionAddress == null) {
+ throw new NoServerForRegionException(
+ "unable to locate root region server");
+ }
+
+ // return the region location
+ return new HRegionLocation(
+ HRegionInfo.ROOT_REGIONINFO, rootRegionAddress);
+ }
+
+ /** {@inheritDoc} */
+ public <T> T getRegionServerWithRetries(ServerCallable<T> callable)
+ throws IOException, RuntimeException {
+ getMaster();
+ List<Throwable> exceptions = new ArrayList<Throwable>();
+ for(int tries = 0; tries < numRetries; tries++) {
+ try {
+ callable.instantiateServer(tries != 0);
+ return callable.call();
+ } catch (Throwable t) {
+ if (t instanceof UndeclaredThrowableException) {
+ t = t.getCause();
+ }
+ if (t instanceof RemoteException) {
+ t = RemoteExceptionHandler.decodeRemoteException((RemoteException) t);
+ }
+ if (t instanceof DoNotRetryIOException) {
+ throw (DoNotRetryIOException)t;
+ }
+ exceptions.add(t);
+ if (tries == numRetries - 1) {
+ throw new RetriesExhaustedException(callable.getServerName(),
+ callable.getRegionName(), callable.getRow(), tries, exceptions);
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("reloading table servers because: " + t.getMessage());
+ }
+ }
+ try {
+ Thread.sleep(getPauseTime(tries));
+ } catch (InterruptedException e) {
+ // continue
+ }
+ }
+ return null;
+ }
+ }
+}