HBASE-17402 TestAsyncTableScan sometimes hangs
This commit is contained in:
parent
9ec0ec4922
commit
af9d359b8e
|
@ -53,7 +53,6 @@ import org.apache.hadoop.hbase.RegionLocations;
|
||||||
import org.apache.hadoop.hbase.TableName;
|
import org.apache.hadoop.hbase.TableName;
|
||||||
import org.apache.hadoop.hbase.TableNotFoundException;
|
import org.apache.hadoop.hbase.TableNotFoundException;
|
||||||
import org.apache.hadoop.hbase.classification.InterfaceAudience;
|
import org.apache.hadoop.hbase.classification.InterfaceAudience;
|
||||||
import org.apache.hadoop.hbase.client.Scan.ReadType;
|
|
||||||
import org.apache.hadoop.hbase.util.Bytes;
|
import org.apache.hadoop.hbase.util.Bytes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -140,8 +139,8 @@ class AsyncNonMetaRegionLocator {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tableCache.cache.computeIfPresent(loc.getRegionInfo().getStartKey(), (k, oldLoc) -> {
|
tableCache.cache.computeIfPresent(loc.getRegionInfo().getStartKey(), (k, oldLoc) -> {
|
||||||
if (oldLoc.getSeqNum() > loc.getSeqNum()
|
if (oldLoc.getSeqNum() > loc.getSeqNum() ||
|
||||||
|| !oldLoc.getServerName().equals(loc.getServerName())) {
|
!oldLoc.getServerName().equals(loc.getServerName())) {
|
||||||
return oldLoc;
|
return oldLoc;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -158,11 +157,11 @@ class AsyncNonMetaRegionLocator {
|
||||||
if (oldLoc == null) {
|
if (oldLoc == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (oldLoc.getSeqNum() > loc.getSeqNum()
|
if (oldLoc.getSeqNum() > loc.getSeqNum() ||
|
||||||
|| oldLoc.getServerName().equals(loc.getServerName())) {
|
oldLoc.getServerName().equals(loc.getServerName())) {
|
||||||
if (LOG.isTraceEnabled()) {
|
if (LOG.isTraceEnabled()) {
|
||||||
LOG.trace("Will not add " + loc + " to cache because the old value " + oldLoc
|
LOG.trace("Will not add " + loc + " to cache because the old value " + oldLoc +
|
||||||
+ " is newer than us or has the same server name");
|
" is newer than us or has the same server name");
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -171,9 +170,9 @@ class AsyncNonMetaRegionLocator {
|
||||||
return loc;
|
return loc;
|
||||||
}
|
}
|
||||||
if (LOG.isTraceEnabled()) {
|
if (LOG.isTraceEnabled()) {
|
||||||
LOG.trace("Will not add " + loc + " to cache because the old value " + oldValue
|
LOG.trace("Will not add " + loc + " to cache because the old value " + oldValue +
|
||||||
+ " is newer than us or has the same server name."
|
" is newer than us or has the same server name." +
|
||||||
+ " Maybe it is updated before we replace it");
|
" Maybe it is updated before we replace it");
|
||||||
}
|
}
|
||||||
return oldValue;
|
return oldValue;
|
||||||
});
|
});
|
||||||
|
@ -217,8 +216,8 @@ class AsyncNonMetaRegionLocator {
|
||||||
Throwable error) {
|
Throwable error) {
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Failed to locate region in '" + tableName + "', row='"
|
LOG.debug("Failed to locate region in '" + tableName + "', row='" +
|
||||||
+ Bytes.toStringBinary(req.row) + "', locateType=" + req.locateType,
|
Bytes.toStringBinary(req.row) + "', locateType=" + req.locateType,
|
||||||
error);
|
error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,14 +249,15 @@ class AsyncNonMetaRegionLocator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!tableCache.allRequests.isEmpty()
|
if (!tableCache.allRequests.isEmpty() &&
|
||||||
&& tableCache.hasQuota(maxConcurrentLocateRequestPerTable)) {
|
tableCache.hasQuota(maxConcurrentLocateRequestPerTable)) {
|
||||||
LocateRequest[] candidates = tableCache.allRequests.keySet().stream()
|
LocateRequest[] candidates = tableCache.allRequests.keySet().stream()
|
||||||
.filter(r -> !tableCache.isPending(r)).toArray(LocateRequest[]::new);
|
.filter(r -> !tableCache.isPending(r)).toArray(LocateRequest[]::new);
|
||||||
if (candidates.length > 0) {
|
if (candidates.length > 0) {
|
||||||
// TODO: use a better algorithm to send a request which is more likely to fetch a new
|
// TODO: use a better algorithm to send a request which is more likely to fetch a new
|
||||||
// location.
|
// location.
|
||||||
toSend = candidates[ThreadLocalRandom.current().nextInt(candidates.length)];
|
toSend = candidates[ThreadLocalRandom.current().nextInt(candidates.length)];
|
||||||
|
tableCache.send(toSend);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,8 +278,8 @@ class AsyncNonMetaRegionLocator {
|
||||||
}
|
}
|
||||||
RegionLocations locs = MetaTableAccessor.getRegionLocations(results.get(0));
|
RegionLocations locs = MetaTableAccessor.getRegionLocations(results.get(0));
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("The fetched location of '" + tableName + "', row='" + Bytes.toStringBinary(req.row)
|
LOG.debug("The fetched location of '" + tableName + "', row='" +
|
||||||
+ "', locateType=" + req.locateType + " is " + locs);
|
Bytes.toStringBinary(req.row) + "', locateType=" + req.locateType + " is " + locs);
|
||||||
}
|
}
|
||||||
if (locs == null || locs.getDefaultRegionLocation() == null) {
|
if (locs == null || locs.getDefaultRegionLocation() == null) {
|
||||||
complete(tableName, req, null,
|
complete(tableName, req, null,
|
||||||
|
@ -303,13 +303,13 @@ class AsyncNonMetaRegionLocator {
|
||||||
if (info.isSplit()) {
|
if (info.isSplit()) {
|
||||||
complete(tableName, req, null,
|
complete(tableName, req, null,
|
||||||
new RegionOfflineException(
|
new RegionOfflineException(
|
||||||
"the only available region for the required row is a split parent,"
|
"the only available region for the required row is a split parent," +
|
||||||
+ " the daughters should be online soon: '" + info.getRegionNameAsString() + "'"));
|
" the daughters should be online soon: '" + info.getRegionNameAsString() + "'"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (info.isOffline()) {
|
if (info.isOffline()) {
|
||||||
complete(tableName, req, null, new RegionOfflineException("the region is offline, could"
|
complete(tableName, req, null, new RegionOfflineException("the region is offline, could" +
|
||||||
+ " be caused by a disable table call: '" + info.getRegionNameAsString() + "'"));
|
" be caused by a disable table call: '" + info.getRegionNameAsString() + "'"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (loc.getServerName() == null) {
|
if (loc.getServerName() == null) {
|
||||||
|
@ -331,8 +331,8 @@ class AsyncNonMetaRegionLocator {
|
||||||
byte[] endKey = loc.getRegionInfo().getEndKey();
|
byte[] endKey = loc.getRegionInfo().getEndKey();
|
||||||
if (isEmptyStopRow(endKey) || Bytes.compareTo(row, endKey) < 0) {
|
if (isEmptyStopRow(endKey) || Bytes.compareTo(row, endKey) < 0) {
|
||||||
if (LOG.isTraceEnabled()) {
|
if (LOG.isTraceEnabled()) {
|
||||||
LOG.trace("Found " + loc + " in cache for '" + tableName + "', row='"
|
LOG.trace("Found " + loc + " in cache for '" + tableName + "', row='" +
|
||||||
+ Bytes.toStringBinary(row) + "', locateType=" + RegionLocateType.CURRENT);
|
Bytes.toStringBinary(row) + "', locateType=" + RegionLocateType.CURRENT);
|
||||||
}
|
}
|
||||||
return loc;
|
return loc;
|
||||||
} else {
|
} else {
|
||||||
|
@ -348,11 +348,11 @@ class AsyncNonMetaRegionLocator {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
HRegionLocation loc = entry.getValue();
|
HRegionLocation loc = entry.getValue();
|
||||||
if (isEmptyStopRow(loc.getRegionInfo().getEndKey())
|
if (isEmptyStopRow(loc.getRegionInfo().getEndKey()) ||
|
||||||
|| Bytes.compareTo(loc.getRegionInfo().getEndKey(), row) >= 0) {
|
Bytes.compareTo(loc.getRegionInfo().getEndKey(), row) >= 0) {
|
||||||
if (LOG.isTraceEnabled()) {
|
if (LOG.isTraceEnabled()) {
|
||||||
LOG.trace("Found " + loc + " in cache for '" + tableName + "', row='"
|
LOG.trace("Found " + loc + " in cache for '" + tableName + "', row='" +
|
||||||
+ Bytes.toStringBinary(row) + "', locateType=" + RegionLocateType.BEFORE);
|
Bytes.toStringBinary(row) + "', locateType=" + RegionLocateType.BEFORE);
|
||||||
}
|
}
|
||||||
return loc;
|
return loc;
|
||||||
} else {
|
} else {
|
||||||
|
@ -362,8 +362,8 @@ class AsyncNonMetaRegionLocator {
|
||||||
|
|
||||||
private void locateInMeta(TableName tableName, LocateRequest req) {
|
private void locateInMeta(TableName tableName, LocateRequest req) {
|
||||||
if (LOG.isTraceEnabled()) {
|
if (LOG.isTraceEnabled()) {
|
||||||
LOG.trace("Try locate '" + tableName + "', row='" + Bytes.toStringBinary(req.row)
|
LOG.trace("Try locate '" + tableName + "', row='" + Bytes.toStringBinary(req.row) +
|
||||||
+ "', locateType=" + req.locateType + " in meta");
|
"', locateType=" + req.locateType + " in meta");
|
||||||
}
|
}
|
||||||
byte[] metaKey;
|
byte[] metaKey;
|
||||||
if (req.locateType.equals(RegionLocateType.BEFORE)) {
|
if (req.locateType.equals(RegionLocateType.BEFORE)) {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.hbase.client;
|
package org.apache.hadoop.hbase.client;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
import static org.apache.hadoop.hbase.HConstants.EMPTY_END_ROW;
|
import static org.apache.hadoop.hbase.HConstants.EMPTY_END_ROW;
|
||||||
import static org.apache.hadoop.hbase.HConstants.EMPTY_START_ROW;
|
import static org.apache.hadoop.hbase.HConstants.EMPTY_START_ROW;
|
||||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||||
|
@ -27,6 +28,8 @@ import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
@ -166,10 +169,7 @@ public class TestAsyncNonMetaRegionLocator {
|
||||||
return endKeys;
|
return endKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
private ServerName[] getLocations(byte[][] startKeys) {
|
||||||
public void testMultiRegionTable() throws IOException, InterruptedException {
|
|
||||||
createMultiRegionTable();
|
|
||||||
byte[][] startKeys = getStartKeys();
|
|
||||||
ServerName[] serverNames = new ServerName[startKeys.length];
|
ServerName[] serverNames = new ServerName[startKeys.length];
|
||||||
TEST_UTIL.getHBaseCluster().getRegionServerThreads().stream().map(t -> t.getRegionServer())
|
TEST_UTIL.getHBaseCluster().getRegionServerThreads().stream().map(t -> t.getRegionServer())
|
||||||
.forEach(rs -> {
|
.forEach(rs -> {
|
||||||
|
@ -178,6 +178,14 @@ public class TestAsyncNonMetaRegionLocator {
|
||||||
Bytes::compareTo)] = rs.getServerName();
|
Bytes::compareTo)] = rs.getServerName();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
return serverNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultiRegionTable() throws IOException, InterruptedException {
|
||||||
|
createMultiRegionTable();
|
||||||
|
byte[][] startKeys = getStartKeys();
|
||||||
|
ServerName[] serverNames = getLocations(startKeys);
|
||||||
IntStream.range(0, 2).forEach(n -> IntStream.range(0, startKeys.length).forEach(i -> {
|
IntStream.range(0, 2).forEach(n -> IntStream.range(0, startKeys.length).forEach(i -> {
|
||||||
try {
|
try {
|
||||||
assertLocEquals(startKeys[i], i == startKeys.length - 1 ? EMPTY_END_ROW : startKeys[i + 1],
|
assertLocEquals(startKeys[i], i == startKeys.length - 1 ? EMPTY_END_ROW : startKeys[i + 1],
|
||||||
|
@ -264,4 +272,24 @@ public class TestAsyncNonMetaRegionLocator {
|
||||||
|
|
||||||
assertSame(afterLoc, LOCATOR.getRegionLocation(TABLE_NAME, row, RegionLocateType.AFTER).get());
|
assertSame(afterLoc, LOCATOR.getRegionLocation(TABLE_NAME, row, RegionLocateType.AFTER).get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For HBASE-17402
|
||||||
|
@Test
|
||||||
|
public void testConcurrentLocate() throws IOException, InterruptedException, ExecutionException {
|
||||||
|
createMultiRegionTable();
|
||||||
|
byte[][] startKeys = getStartKeys();
|
||||||
|
byte[][] endKeys = getEndKeys();
|
||||||
|
ServerName[] serverNames = getLocations(startKeys);
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
LOCATOR.clearCache(TABLE_NAME);
|
||||||
|
List<CompletableFuture<HRegionLocation>> futures = IntStream.range(0, 1000)
|
||||||
|
.mapToObj(n -> String.format("%03d", n)).map(s -> Bytes.toBytes(s))
|
||||||
|
.map(r -> LOCATOR.getRegionLocation(TABLE_NAME, r, RegionLocateType.CURRENT))
|
||||||
|
.collect(toList());
|
||||||
|
for (int j = 0; j < 1000; j++) {
|
||||||
|
int index = Math.min(8, j / 111);
|
||||||
|
assertLocEquals(startKeys[index], endKeys[index], serverNames[index], futures.get(j).get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue