HBASE-9861 Location does not have to be refreshed on regionTooBusy

git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1537425 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
nkeywal 2013-10-31 10:29:30 +00:00
parent 68db456397
commit e828b5eb69
4 changed files with 82 additions and 99 deletions

View File

@ -56,6 +56,7 @@ import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.HRegionLocation;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.MasterNotRunningException;
import org.apache.hadoop.hbase.RegionTooBusyException;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.Stoppable;
import org.apache.hadoop.hbase.TableName;
@ -2208,7 +2209,8 @@ public class HConnectionManager {
/**
* Update the location with the new value (if the exception is a RegionMovedException)
* or delete it from the cache.
* or delete it from the cache. Does nothing if we can be sure from the exception that
* the location is still accurate, or if the cache has already been updated.
* @param exception an object (to simplify user code) on which we will try to find a nested
* or wrapped or both RegionMovedException
* @param source server that is the source of the location update.
@ -2222,30 +2224,45 @@ public class HConnectionManager {
return;
}
if (source == null || source.getServerName() == null){
// This should not happen, but let's secure ourselves.
return;
}
// Is it something we have already updated?
final HRegionLocation oldLocation = getCachedLocation(tableName, rowkey);
if (oldLocation == null) {
// There is no such location in the cache => it's been removed already => nothing to do
if (oldLocation == null || !source.getServerName().equals(oldLocation.getServerName())) {
// There is no such location in the cache (it's been removed already) or
// the cache has already been refreshed with a different location. => nothing to do
return;
}
HRegionInfo regionInfo = oldLocation.getRegionInfo();
final RegionMovedException rme = RegionMovedException.find(exception);
if (rme != null) {
if (LOG.isTraceEnabled()){
LOG.trace("Region " + regionInfo.getRegionNameAsString() + " moved to " +
rme.getHostname() + ":" + rme.getPort() + " according to " + source.getHostnamePort());
Throwable cause = findException(exception);
if (cause != null) {
if (cause instanceof RegionTooBusyException || cause instanceof RegionOpeningException) {
// We know that the region is still on this region server
return;
}
updateCachedLocation(
regionInfo, source, rme.getServerName(), rme.getLocationSeqNum());
} else if (RegionOpeningException.find(exception) != null) {
if (LOG.isTraceEnabled()) {
LOG.trace("Region " + regionInfo.getRegionNameAsString() + " is being opened on "
+ source.getHostnamePort() + "; not deleting the cache entry");
if (cause instanceof RegionMovedException) {
RegionMovedException rme = (RegionMovedException) cause;
if (LOG.isTraceEnabled()) {
LOG.trace("Region " + regionInfo.getRegionNameAsString() + " moved to " +
rme.getHostname() + ":" + rme.getPort() +
" according to " + source.getHostnamePort());
}
// We know that the region is not anymore on this region server, but we know
// the new location.
updateCachedLocation(
regionInfo, source, rme.getServerName(), rme.getLocationSeqNum());
return;
}
} else {
deleteCachedLocation(regionInfo, source);
}
// If we're here, it means that can cannot be sure about the location, so we remove it from
// the cache.
deleteCachedLocation(regionInfo, source);
}
@Override
@ -2713,6 +2730,46 @@ public class HConnectionManager {
}
}
/**
* Look for an exception we know in the remote exception:
* - hadoop.ipc wrapped exceptions
* - nested exceptions
*
* Looks for: RegionMovedException / RegionOpeningException / RegionTooBusyException
* @returns null if we didn't find the exception, the exception otherwise.
*/
public static Throwable findException(Object exception) {
if (exception == null || !(exception instanceof Throwable)) {
return null;
}
Throwable cur = (Throwable) exception;
while (cur != null) {
if (cur instanceof RegionMovedException || cur instanceof RegionOpeningException
|| cur instanceof RegionTooBusyException) {
return cur;
}
if (cur instanceof RemoteException) {
RemoteException re = (RemoteException) cur;
cur = re.unwrapRemoteException(
RegionOpeningException.class, RegionMovedException.class,
RegionTooBusyException.class);
if (cur == null) {
cur = re.unwrapRemoteException();
}
// unwrapRemoteException can return the exception given as a parameter when it cannot
// unwrap it. In this case, there is no need to look further
// noinspection ObjectEquality
if (cur == re) {
return null;
}
} else {
cur = cur.getCause();
}
}
return null;
}
/**
* Set the number of retries to use serverside when trying to communicate
* with another server over {@link HConnection}. Used updating catalog

View File

@ -109,47 +109,4 @@ public class RegionMovedException extends NotServingRegionException {
return "Region moved to: " + HOST_FIELD + hostname + " " + PORT_FIELD + port + " " +
STARTCODE_FIELD + startCode + ". As of " + LOCATIONSEQNUM_FIELD + locationSeqNum + ".";
}
/**
* Look for a RegionMovedException in the exception:
* - hadoop.ipc wrapped exceptions
* - nested exceptions
* Returns null if we didn't find the exception or if it was not readable.
*/
public static RegionMovedException find(Object exception) {
if (exception == null || !(exception instanceof Throwable)){
return null;
}
Throwable cur = (Throwable)exception;
RegionMovedException res = null;
while (res == null && cur != null) {
if (cur instanceof RegionMovedException) {
res = (RegionMovedException) cur;
} else {
if (cur instanceof RemoteException) {
RemoteException re = (RemoteException) cur;
Exception e = re.unwrapRemoteException(RegionMovedException.class);
if (e == null){
e = re.unwrapRemoteException();
}
// unwrapRemoteException can return the exception given as a parameter when it cannot
// unwrap it. In this case, there is no need to look further
// noinspection ObjectEquality
if (e != re){
res = find(e);
}
}
cur = cur.getCause();
}
}
if (res != null && (res.getPort() < 0 || res.getHostname() == null)){
// We failed to parse the exception. Let's act as we don't find the exception.
return null;
} else {
return res;
}
}
}

View File

@ -38,42 +38,4 @@ public class RegionOpeningException extends NotServingRegionException {
public RegionOpeningException(String message) {
super(message);
}
/**
* Look for a RegionOpeningException in the exception:
* - hadoop.ipc wrapped exceptions
* - nested exceptions
* Returns null if we didn't find the exception.
* TODO: this code is mostly C/Ped from RegionMovedExecption. Due to the limitations of
* generics it's not amenable to generalizing without adding parameters/isAssignableFrom.
* Might make general if used in more places.
*/
public static RegionOpeningException find(Object exception) {
if (exception == null || !(exception instanceof Throwable)) {
return null;
}
RegionOpeningException res = null;
Throwable cur = (Throwable)exception;
while (res == null && cur != null) {
if (cur instanceof RegionOpeningException) {
res = (RegionOpeningException) cur;
} else {
if (cur instanceof RemoteException) {
RemoteException re = (RemoteException) cur;
Exception e = re.unwrapRemoteException(RegionOpeningException.class);
if (e == null) {
e = re.unwrapRemoteException();
}
// unwrapRemoteException can return the exception given as a parameter when it cannot
// unwrap it. In this case, there is no need to look further
// noinspection ObjectEquality
if (e != re) {
res = find(e);
}
}
cur = cur.getCause();
}
}
return res;
}
}

View File

@ -54,6 +54,7 @@ import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.Waiter;
import org.apache.hadoop.hbase.client.HConnectionManager.HConnectionImplementation;
import org.apache.hadoop.hbase.exceptions.DeserializationException;
import org.apache.hadoop.hbase.exceptions.RegionMovedException;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.FilterBase;
import org.apache.hadoop.hbase.master.HMaster;
@ -449,12 +450,18 @@ public class TestHCM {
} catch (RetriesExhaustedWithDetailsException e){
LOG.info("Put done, exception caught: " + e.getClass());
Assert.assertEquals(1, e.getNumExceptions());
Assert.assertEquals(1, e.getCauses().size());
Assert.assertArrayEquals(e.getRow(0).getRow(), ROW);
// Check that we unserialized the exception as expected
Throwable cause = HConnectionManager.findException(e.getCause(0));
Assert.assertNotNull(cause);
Assert.assertTrue(cause instanceof RegionMovedException);
}
Assert.assertNotNull("Cached connection is null", conn.getCachedLocation(TABLE_NAME, ROW));
Assert.assertEquals(
"Previous server was "+curServer.getServerName().getHostAndPort(),
destServerName.getPort(), conn.getCachedLocation(TABLE_NAME, ROW).getPort());
"Previous server was " + curServer.getServerName().getHostAndPort(),
destServerName.getPort(), conn.getCachedLocation(TABLE_NAME, ROW).getPort());
Assert.assertFalse(destServer.getRegionsInTransitionInRS().containsKey(encodedRegionNameBytes));
Assert.assertFalse(curServer.getRegionsInTransitionInRS().containsKey(encodedRegionNameBytes));