HBASE-17691 Add ScanMetrics support for async scan
This commit is contained in:
parent
7c03a213ff
commit
5b4bb8217d
|
@ -19,8 +19,7 @@ package org.apache.hadoop.hbase.client;
|
||||||
|
|
||||||
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.apache.hadoop.hbase.client.ConnectionUtils.createScanResultCache;
|
import static org.apache.hadoop.hbase.client.ConnectionUtils.*;
|
||||||
import static org.apache.hadoop.hbase.client.ConnectionUtils.getLocateType;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
@ -29,6 +28,7 @@ import java.util.concurrent.TimeUnit;
|
||||||
import org.apache.hadoop.hbase.HRegionLocation;
|
import org.apache.hadoop.hbase.HRegionLocation;
|
||||||
import org.apache.hadoop.hbase.TableName;
|
import org.apache.hadoop.hbase.TableName;
|
||||||
import org.apache.hadoop.hbase.classification.InterfaceAudience;
|
import org.apache.hadoop.hbase.classification.InterfaceAudience;
|
||||||
|
import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
|
||||||
import org.apache.hadoop.hbase.ipc.HBaseRpcController;
|
import org.apache.hadoop.hbase.ipc.HBaseRpcController;
|
||||||
import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter;
|
import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter;
|
||||||
import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ClientService;
|
import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ClientService;
|
||||||
|
@ -51,6 +51,8 @@ class AsyncClientScanner {
|
||||||
// AsyncScanSingleRegionRpcRetryingCaller will modify this scan object directly.
|
// AsyncScanSingleRegionRpcRetryingCaller will modify this scan object directly.
|
||||||
private final Scan scan;
|
private final Scan scan;
|
||||||
|
|
||||||
|
private final ScanMetrics scanMetrics;
|
||||||
|
|
||||||
private final RawScanResultConsumer consumer;
|
private final RawScanResultConsumer consumer;
|
||||||
|
|
||||||
private final TableName tableName;
|
private final TableName tableName;
|
||||||
|
@ -88,29 +90,46 @@ class AsyncClientScanner {
|
||||||
this.rpcTimeoutNs = rpcTimeoutNs;
|
this.rpcTimeoutNs = rpcTimeoutNs;
|
||||||
this.startLogErrorsCnt = startLogErrorsCnt;
|
this.startLogErrorsCnt = startLogErrorsCnt;
|
||||||
this.resultCache = createScanResultCache(scan);
|
this.resultCache = createScanResultCache(scan);
|
||||||
|
if (scan.isScanMetricsEnabled()) {
|
||||||
|
this.scanMetrics = new ScanMetrics();
|
||||||
|
consumer.onScanMetricsCreated(scanMetrics);
|
||||||
|
} else {
|
||||||
|
this.scanMetrics = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class OpenScannerResponse {
|
private static final class OpenScannerResponse {
|
||||||
|
|
||||||
public final HRegionLocation loc;
|
public final HRegionLocation loc;
|
||||||
|
|
||||||
|
public final boolean isRegionServerRemote;
|
||||||
|
|
||||||
public final ClientService.Interface stub;
|
public final ClientService.Interface stub;
|
||||||
|
|
||||||
public final HBaseRpcController controller;
|
public final HBaseRpcController controller;
|
||||||
|
|
||||||
public final ScanResponse resp;
|
public final ScanResponse resp;
|
||||||
|
|
||||||
public OpenScannerResponse(HRegionLocation loc, Interface stub, HBaseRpcController controller,
|
public OpenScannerResponse(HRegionLocation loc, boolean isRegionServerRemote, Interface stub,
|
||||||
ScanResponse resp) {
|
HBaseRpcController controller, ScanResponse resp) {
|
||||||
this.loc = loc;
|
this.loc = loc;
|
||||||
|
this.isRegionServerRemote = isRegionServerRemote;
|
||||||
this.stub = stub;
|
this.stub = stub;
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
this.resp = resp;
|
this.resp = resp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int openScannerTries;
|
||||||
|
|
||||||
private CompletableFuture<OpenScannerResponse> callOpenScanner(HBaseRpcController controller,
|
private CompletableFuture<OpenScannerResponse> callOpenScanner(HBaseRpcController controller,
|
||||||
HRegionLocation loc, ClientService.Interface stub) {
|
HRegionLocation loc, ClientService.Interface stub) {
|
||||||
|
boolean isRegionServerRemote = isRemote(loc.getHostname());
|
||||||
|
incRPCCallsMetrics(scanMetrics, isRegionServerRemote);
|
||||||
|
if (openScannerTries > 1) {
|
||||||
|
incRPCRetriesMetrics(scanMetrics, isRegionServerRemote);
|
||||||
|
}
|
||||||
|
openScannerTries++;
|
||||||
CompletableFuture<OpenScannerResponse> future = new CompletableFuture<>();
|
CompletableFuture<OpenScannerResponse> future = new CompletableFuture<>();
|
||||||
try {
|
try {
|
||||||
ScanRequest request = RequestConverter.buildScanRequest(loc.getRegionInfo().getRegionName(),
|
ScanRequest request = RequestConverter.buildScanRequest(loc.getRegionInfo().getRegionName(),
|
||||||
|
@ -120,7 +139,7 @@ class AsyncClientScanner {
|
||||||
future.completeExceptionally(controller.getFailed());
|
future.completeExceptionally(controller.getFailed());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
future.complete(new OpenScannerResponse(loc, stub, controller, resp));
|
future.complete(new OpenScannerResponse(loc, isRegionServerRemote, stub, controller, resp));
|
||||||
});
|
});
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
future.completeExceptionally(e);
|
future.completeExceptionally(e);
|
||||||
|
@ -130,8 +149,9 @@ class AsyncClientScanner {
|
||||||
|
|
||||||
private void startScan(OpenScannerResponse resp) {
|
private void startScan(OpenScannerResponse resp) {
|
||||||
conn.callerFactory.scanSingleRegion().id(resp.resp.getScannerId()).location(resp.loc)
|
conn.callerFactory.scanSingleRegion().id(resp.resp.getScannerId()).location(resp.loc)
|
||||||
|
.remote(resp.isRegionServerRemote)
|
||||||
.scannerLeaseTimeoutPeriod(resp.resp.getTtl(), TimeUnit.MILLISECONDS).stub(resp.stub)
|
.scannerLeaseTimeoutPeriod(resp.resp.getTtl(), TimeUnit.MILLISECONDS).stub(resp.stub)
|
||||||
.setScan(scan).consumer(consumer).resultCache(resultCache)
|
.setScan(scan).metrics(scanMetrics).consumer(consumer).resultCache(resultCache)
|
||||||
.rpcTimeout(rpcTimeoutNs, TimeUnit.NANOSECONDS)
|
.rpcTimeout(rpcTimeoutNs, TimeUnit.NANOSECONDS)
|
||||||
.scanTimeout(scanTimeoutNs, TimeUnit.NANOSECONDS).pause(pauseNs, TimeUnit.NANOSECONDS)
|
.scanTimeout(scanTimeoutNs, TimeUnit.NANOSECONDS).pause(pauseNs, TimeUnit.NANOSECONDS)
|
||||||
.maxAttempts(maxAttempts).startLogErrorsCnt(startLogErrorsCnt)
|
.maxAttempts(maxAttempts).startLogErrorsCnt(startLogErrorsCnt)
|
||||||
|
@ -149,6 +169,8 @@ class AsyncClientScanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openScanner() {
|
private void openScanner() {
|
||||||
|
incRegionCountMetrics(scanMetrics);
|
||||||
|
openScannerTries = 1;
|
||||||
conn.callerFactory.<OpenScannerResponse> single().table(tableName).row(scan.getStartRow())
|
conn.callerFactory.<OpenScannerResponse> single().table(tableName).row(scan.getStartRow())
|
||||||
.locateType(getLocateType(scan)).rpcTimeout(rpcTimeoutNs, TimeUnit.NANOSECONDS)
|
.locateType(getLocateType(scan)).rpcTimeout(rpcTimeoutNs, TimeUnit.NANOSECONDS)
|
||||||
.operationTimeout(scanTimeoutNs, TimeUnit.NANOSECONDS).pause(pauseNs, TimeUnit.NANOSECONDS)
|
.operationTimeout(scanTimeoutNs, TimeUnit.NANOSECONDS).pause(pauseNs, TimeUnit.NANOSECONDS)
|
||||||
|
|
|
@ -19,7 +19,7 @@ package org.apache.hadoop.hbase.client;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static org.apache.hadoop.hbase.client.ConnectionUtils.*;
|
import static org.apache.hadoop.hbase.client.ConnectionUtils.retries2Attempts;
|
||||||
|
|
||||||
import io.netty.util.HashedWheelTimer;
|
import io.netty.util.HashedWheelTimer;
|
||||||
|
|
||||||
|
@ -31,10 +31,10 @@ import org.apache.hadoop.hbase.HRegionLocation;
|
||||||
import org.apache.hadoop.hbase.ServerName;
|
import org.apache.hadoop.hbase.ServerName;
|
||||||
import org.apache.hadoop.hbase.TableName;
|
import org.apache.hadoop.hbase.TableName;
|
||||||
import org.apache.hadoop.hbase.classification.InterfaceAudience;
|
import org.apache.hadoop.hbase.classification.InterfaceAudience;
|
||||||
|
import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
|
||||||
import org.apache.hadoop.hbase.ipc.HBaseRpcController;
|
import org.apache.hadoop.hbase.ipc.HBaseRpcController;
|
||||||
import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ClientService;
|
import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ClientService;
|
||||||
import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ScanResponse;
|
import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ScanResponse;
|
||||||
import org.apache.hadoop.ipc.ProtobufRpcEngine.Server;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory to create an AsyncRpcRetryCaller.
|
* Factory to create an AsyncRpcRetryCaller.
|
||||||
|
@ -148,6 +148,8 @@ class AsyncRpcRetryingCallerFactory {
|
||||||
|
|
||||||
private Scan scan;
|
private Scan scan;
|
||||||
|
|
||||||
|
private ScanMetrics scanMetrics;
|
||||||
|
|
||||||
private ScanResultCache resultCache;
|
private ScanResultCache resultCache;
|
||||||
|
|
||||||
private RawScanResultConsumer consumer;
|
private RawScanResultConsumer consumer;
|
||||||
|
@ -156,6 +158,8 @@ class AsyncRpcRetryingCallerFactory {
|
||||||
|
|
||||||
private HRegionLocation loc;
|
private HRegionLocation loc;
|
||||||
|
|
||||||
|
private boolean isRegionServerRemote;
|
||||||
|
|
||||||
private long scannerLeaseTimeoutPeriodNs;
|
private long scannerLeaseTimeoutPeriodNs;
|
||||||
|
|
||||||
private long scanTimeoutNs;
|
private long scanTimeoutNs;
|
||||||
|
@ -172,6 +176,16 @@ class AsyncRpcRetryingCallerFactory {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ScanSingleRegionCallerBuilder metrics(ScanMetrics scanMetrics) {
|
||||||
|
this.scanMetrics = scanMetrics;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScanSingleRegionCallerBuilder remote(boolean isRegionServerRemote) {
|
||||||
|
this.isRegionServerRemote = isRegionServerRemote;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public ScanSingleRegionCallerBuilder resultCache(ScanResultCache resultCache) {
|
public ScanSingleRegionCallerBuilder resultCache(ScanResultCache resultCache) {
|
||||||
this.resultCache = resultCache;
|
this.resultCache = resultCache;
|
||||||
return this;
|
return this;
|
||||||
|
@ -226,11 +240,11 @@ class AsyncRpcRetryingCallerFactory {
|
||||||
public AsyncScanSingleRegionRpcRetryingCaller build() {
|
public AsyncScanSingleRegionRpcRetryingCaller build() {
|
||||||
checkArgument(scannerId >= 0, "invalid scannerId %d", scannerId);
|
checkArgument(scannerId >= 0, "invalid scannerId %d", scannerId);
|
||||||
return new AsyncScanSingleRegionRpcRetryingCaller(retryTimer, conn,
|
return new AsyncScanSingleRegionRpcRetryingCaller(retryTimer, conn,
|
||||||
checkNotNull(scan, "scan is null"), scannerId,
|
checkNotNull(scan, "scan is null"), scanMetrics, scannerId,
|
||||||
checkNotNull(resultCache, "resultCache is null"),
|
checkNotNull(resultCache, "resultCache is null"),
|
||||||
checkNotNull(consumer, "consumer is null"), checkNotNull(stub, "stub is null"),
|
checkNotNull(consumer, "consumer is null"), checkNotNull(stub, "stub is null"),
|
||||||
checkNotNull(loc, "location is null"), scannerLeaseTimeoutPeriodNs, pauseNs, maxAttempts,
|
checkNotNull(loc, "location is null"), isRegionServerRemote, scannerLeaseTimeoutPeriodNs,
|
||||||
scanTimeoutNs, rpcTimeoutNs, startLogErrorsCnt);
|
pauseNs, maxAttempts, scanTimeoutNs, rpcTimeoutNs, startLogErrorsCnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.hbase.client;
|
package org.apache.hadoop.hbase.client;
|
||||||
|
|
||||||
import static org.apache.hadoop.hbase.client.ConnectionUtils.SLEEP_DELTA_NS;
|
import static org.apache.hadoop.hbase.client.ConnectionUtils.*;
|
||||||
import static org.apache.hadoop.hbase.client.ConnectionUtils.getPauseTime;
|
import static org.apache.hadoop.hbase.client.ConnectionUtils.getPauseTime;
|
||||||
import static org.apache.hadoop.hbase.client.ConnectionUtils.noMoreResultsForReverseScan;
|
import static org.apache.hadoop.hbase.client.ConnectionUtils.noMoreResultsForReverseScan;
|
||||||
import static org.apache.hadoop.hbase.client.ConnectionUtils.noMoreResultsForScan;
|
import static org.apache.hadoop.hbase.client.ConnectionUtils.noMoreResultsForScan;
|
||||||
|
@ -46,6 +46,7 @@ import org.apache.hadoop.hbase.NotServingRegionException;
|
||||||
import org.apache.hadoop.hbase.UnknownScannerException;
|
import org.apache.hadoop.hbase.UnknownScannerException;
|
||||||
import org.apache.hadoop.hbase.classification.InterfaceAudience;
|
import org.apache.hadoop.hbase.classification.InterfaceAudience;
|
||||||
import org.apache.hadoop.hbase.client.RawScanResultConsumer.ScanResumer;
|
import org.apache.hadoop.hbase.client.RawScanResultConsumer.ScanResumer;
|
||||||
|
import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
|
||||||
import org.apache.hadoop.hbase.exceptions.OutOfOrderScannerNextException;
|
import org.apache.hadoop.hbase.exceptions.OutOfOrderScannerNextException;
|
||||||
import org.apache.hadoop.hbase.exceptions.ScannerResetException;
|
import org.apache.hadoop.hbase.exceptions.ScannerResetException;
|
||||||
import org.apache.hadoop.hbase.ipc.HBaseRpcController;
|
import org.apache.hadoop.hbase.ipc.HBaseRpcController;
|
||||||
|
@ -73,6 +74,8 @@ class AsyncScanSingleRegionRpcRetryingCaller {
|
||||||
|
|
||||||
private final Scan scan;
|
private final Scan scan;
|
||||||
|
|
||||||
|
private final ScanMetrics scanMetrics;
|
||||||
|
|
||||||
private final long scannerId;
|
private final long scannerId;
|
||||||
|
|
||||||
private final ScanResultCache resultCache;
|
private final ScanResultCache resultCache;
|
||||||
|
@ -83,6 +86,8 @@ class AsyncScanSingleRegionRpcRetryingCaller {
|
||||||
|
|
||||||
private final HRegionLocation loc;
|
private final HRegionLocation loc;
|
||||||
|
|
||||||
|
private final boolean regionServerRemote;
|
||||||
|
|
||||||
private final long scannerLeaseTimeoutPeriodNs;
|
private final long scannerLeaseTimeoutPeriodNs;
|
||||||
|
|
||||||
private final long pauseNs;
|
private final long pauseNs;
|
||||||
|
@ -107,7 +112,7 @@ class AsyncScanSingleRegionRpcRetryingCaller {
|
||||||
|
|
||||||
private long nextCallStartNs;
|
private long nextCallStartNs;
|
||||||
|
|
||||||
private int tries = 1;
|
private int tries;
|
||||||
|
|
||||||
private final List<RetriesExhaustedException.ThrowableWithExtraContext> exceptions;
|
private final List<RetriesExhaustedException.ThrowableWithExtraContext> exceptions;
|
||||||
|
|
||||||
|
@ -279,17 +284,19 @@ class AsyncScanSingleRegionRpcRetryingCaller {
|
||||||
}
|
}
|
||||||
|
|
||||||
public AsyncScanSingleRegionRpcRetryingCaller(HashedWheelTimer retryTimer,
|
public AsyncScanSingleRegionRpcRetryingCaller(HashedWheelTimer retryTimer,
|
||||||
AsyncConnectionImpl conn, Scan scan, long scannerId, ScanResultCache resultCache,
|
AsyncConnectionImpl conn, Scan scan, ScanMetrics scanMetrics, long scannerId,
|
||||||
RawScanResultConsumer consumer, Interface stub, HRegionLocation loc,
|
ScanResultCache resultCache, RawScanResultConsumer consumer, Interface stub,
|
||||||
long scannerLeaseTimeoutPeriodNs, long pauseNs, int maxAttempts, long scanTimeoutNs,
|
HRegionLocation loc, boolean isRegionServerRemote, long scannerLeaseTimeoutPeriodNs,
|
||||||
long rpcTimeoutNs, int startLogErrorsCnt) {
|
long pauseNs, int maxAttempts, long scanTimeoutNs, long rpcTimeoutNs, int startLogErrorsCnt) {
|
||||||
this.retryTimer = retryTimer;
|
this.retryTimer = retryTimer;
|
||||||
this.scan = scan;
|
this.scan = scan;
|
||||||
|
this.scanMetrics = scanMetrics;
|
||||||
this.scannerId = scannerId;
|
this.scannerId = scannerId;
|
||||||
this.resultCache = resultCache;
|
this.resultCache = resultCache;
|
||||||
this.consumer = consumer;
|
this.consumer = consumer;
|
||||||
this.stub = stub;
|
this.stub = stub;
|
||||||
this.loc = loc;
|
this.loc = loc;
|
||||||
|
this.regionServerRemote = isRegionServerRemote;
|
||||||
this.scannerLeaseTimeoutPeriodNs = scannerLeaseTimeoutPeriodNs;
|
this.scannerLeaseTimeoutPeriodNs = scannerLeaseTimeoutPeriodNs;
|
||||||
this.pauseNs = pauseNs;
|
this.pauseNs = pauseNs;
|
||||||
this.maxAttempts = maxAttempts;
|
this.maxAttempts = maxAttempts;
|
||||||
|
@ -315,6 +322,7 @@ class AsyncScanSingleRegionRpcRetryingCaller {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void closeScanner() {
|
private void closeScanner() {
|
||||||
|
incRPCCallsMetrics(scanMetrics, regionServerRemote);
|
||||||
resetController(controller, rpcTimeoutNs);
|
resetController(controller, rpcTimeoutNs);
|
||||||
ScanRequest req = RequestConverter.buildScanRequest(this.scannerId, 0, true, false);
|
ScanRequest req = RequestConverter.buildScanRequest(this.scannerId, 0, true, false);
|
||||||
stub.scan(controller, req, resp -> {
|
stub.scan(controller, req, resp -> {
|
||||||
|
@ -345,6 +353,7 @@ class AsyncScanSingleRegionRpcRetryingCaller {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void completeWhenError(boolean closeScanner) {
|
private void completeWhenError(boolean closeScanner) {
|
||||||
|
incRPCRetriesMetrics(scanMetrics, closeScanner);
|
||||||
resultCache.clear();
|
resultCache.clear();
|
||||||
if (closeScanner) {
|
if (closeScanner) {
|
||||||
closeScanner();
|
closeScanner();
|
||||||
|
@ -449,12 +458,14 @@ class AsyncScanSingleRegionRpcRetryingCaller {
|
||||||
onError(controller.getFailed());
|
onError(controller.getFailed());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
updateServerSideMetrics(scanMetrics, resp);
|
||||||
boolean isHeartbeatMessage = resp.hasHeartbeatMessage() && resp.getHeartbeatMessage();
|
boolean isHeartbeatMessage = resp.hasHeartbeatMessage() && resp.getHeartbeatMessage();
|
||||||
Result[] results;
|
Result[] results;
|
||||||
try {
|
try {
|
||||||
|
Result[] rawResults = ResponseConverter.getResults(controller.cellScanner(), resp);
|
||||||
|
updateResultsMetrics(scanMetrics, rawResults, isHeartbeatMessage);
|
||||||
results = resultCache.addAndGet(
|
results = resultCache.addAndGet(
|
||||||
Optional.ofNullable(ResponseConverter.getResults(controller.cellScanner(), resp))
|
Optional.ofNullable(rawResults).orElse(ScanResultCache.EMPTY_RESULT_ARRAY),
|
||||||
.orElse(ScanResultCache.EMPTY_RESULT_ARRAY),
|
|
||||||
isHeartbeatMessage);
|
isHeartbeatMessage);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// We can not retry here. The server has responded normally and the call sequence has been
|
// We can not retry here. The server has responded normally and the call sequence has been
|
||||||
|
@ -464,6 +475,7 @@ class AsyncScanSingleRegionRpcRetryingCaller {
|
||||||
completeWhenError(true);
|
completeWhenError(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate this before calling onNext as it is free for user to modify the result array in
|
// calculate this before calling onNext as it is free for user to modify the result array in
|
||||||
// onNext.
|
// onNext.
|
||||||
int numberOfIndividualRows = numberOfIndividualRows(Arrays.asList(results));
|
int numberOfIndividualRows = numberOfIndividualRows(Arrays.asList(results));
|
||||||
|
@ -510,6 +522,10 @@ class AsyncScanSingleRegionRpcRetryingCaller {
|
||||||
} else {
|
} else {
|
||||||
callTimeoutNs = 0L;
|
callTimeoutNs = 0L;
|
||||||
}
|
}
|
||||||
|
incRPCCallsMetrics(scanMetrics, regionServerRemote);
|
||||||
|
if (tries > 1) {
|
||||||
|
incRPCRetriesMetrics(scanMetrics, regionServerRemote);
|
||||||
|
}
|
||||||
resetController(controller, callTimeoutNs);
|
resetController(controller, callTimeoutNs);
|
||||||
ScanRequest req = RequestConverter.buildScanRequest(scannerId, scan.getCaching(), false,
|
ScanRequest req = RequestConverter.buildScanRequest(scannerId, scan.getCaching(), false,
|
||||||
nextCallSeq, false, false, scan.getLimit());
|
nextCallSeq, false, false, scan.getLimit());
|
||||||
|
@ -518,13 +534,14 @@ class AsyncScanSingleRegionRpcRetryingCaller {
|
||||||
|
|
||||||
private void next() {
|
private void next() {
|
||||||
nextCallSeq++;
|
nextCallSeq++;
|
||||||
tries = 0;
|
tries = 1;
|
||||||
exceptions.clear();
|
exceptions.clear();
|
||||||
nextCallStartNs = System.nanoTime();
|
nextCallStartNs = System.nanoTime();
|
||||||
call();
|
call();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renewLease() {
|
private void renewLease() {
|
||||||
|
incRPCCallsMetrics(scanMetrics, regionServerRemote);
|
||||||
nextCallSeq++;
|
nextCallSeq++;
|
||||||
resetController(controller, rpcTimeoutNs);
|
resetController(controller, rpcTimeoutNs);
|
||||||
ScanRequest req =
|
ScanRequest req =
|
||||||
|
|
|
@ -322,7 +322,14 @@ public interface AsyncTableBase {
|
||||||
* If your result set is very large, you should use other scan method to get a scanner or use
|
* If your result set is very large, you should use other scan method to get a scanner or use
|
||||||
* callback to process the results. They will do chunking to prevent OOM. The scanAll method will
|
* callback to process the results. They will do chunking to prevent OOM. The scanAll method will
|
||||||
* fetch all the results and store them in a List and then return the list to you.
|
* fetch all the results and store them in a List and then return the list to you.
|
||||||
* @param scan A configured {@link Scan} object. SO if you use this method to fetch a really large
|
* <p>
|
||||||
|
* The scan metrics will be collected background if you enable it but you have no way to get it.
|
||||||
|
* Usually you can get scan metrics from {@code ResultScanner}, or through
|
||||||
|
* {@code ScanResultConsumer.onScanMetricsCreated} but this method only returns a list of results.
|
||||||
|
* So if you really care about scan metrics then you'd better use other scan methods which return
|
||||||
|
* a {@code ResultScanner} or let you pass in a {@code ScanResultConsumer}. There is no
|
||||||
|
* performance difference between these scan methods so do not worry.
|
||||||
|
* @param scan A configured {@link Scan} object. So if you use this method to fetch a really large
|
||||||
* result set, it is likely to cause OOM.
|
* result set, it is likely to cause OOM.
|
||||||
* @return The results of this small scan operation. The return value will be wrapped by a
|
* @return The results of this small scan operation. The return value will be wrapped by a
|
||||||
* {@link CompletableFuture}.
|
* {@link CompletableFuture}.
|
||||||
|
|
|
@ -162,6 +162,7 @@ class AsyncTableImpl implements AsyncTable {
|
||||||
|
|
||||||
private void scan0(Scan scan, ScanResultConsumer consumer) {
|
private void scan0(Scan scan, ScanResultConsumer consumer) {
|
||||||
try (ResultScanner scanner = getScanner(scan)) {
|
try (ResultScanner scanner = getScanner(scan)) {
|
||||||
|
consumer.onScanMetricsCreated(scanner.getScanMetrics());
|
||||||
for (Result result; (result = scanner.next()) != null;) {
|
for (Result result; (result = scanner.next()) != null;) {
|
||||||
if (!consumer.onNext(result)) {
|
if (!consumer.onNext(result)) {
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -48,6 +48,8 @@ class AsyncTableResultScanner implements ResultScanner, RawScanResultConsumer {
|
||||||
|
|
||||||
private final Queue<Result> queue = new ArrayDeque<>();
|
private final Queue<Result> queue = new ArrayDeque<>();
|
||||||
|
|
||||||
|
private ScanMetrics scanMetrics;
|
||||||
|
|
||||||
private long cacheSize;
|
private long cacheSize;
|
||||||
|
|
||||||
private boolean closed = false;
|
private boolean closed = false;
|
||||||
|
@ -110,6 +112,11 @@ class AsyncTableResultScanner implements ResultScanner, RawScanResultConsumer {
|
||||||
notifyAll();
|
notifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScanMetricsCreated(ScanMetrics scanMetrics) {
|
||||||
|
this.scanMetrics = scanMetrics;
|
||||||
|
}
|
||||||
|
|
||||||
private void resumePrefetch() {
|
private void resumePrefetch() {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug(String.format("0x%x", System.identityHashCode(this)) + " resume prefetching");
|
LOG.debug(String.format("0x%x", System.identityHashCode(this)) + " resume prefetching");
|
||||||
|
@ -168,6 +175,6 @@ class AsyncTableResultScanner implements ResultScanner, RawScanResultConsumer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ScanMetrics getScanMetrics() {
|
public ScanMetrics getScanMetrics() {
|
||||||
throw new UnsupportedOperationException();
|
return scanMetrics;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.apache.hadoop.hbase.client;
|
||||||
|
|
||||||
import static org.apache.hadoop.hbase.client.ConnectionUtils.calcEstimatedSize;
|
import static org.apache.hadoop.hbase.client.ConnectionUtils.calcEstimatedSize;
|
||||||
import static org.apache.hadoop.hbase.client.ConnectionUtils.createScanResultCache;
|
import static org.apache.hadoop.hbase.client.ConnectionUtils.createScanResultCache;
|
||||||
|
import static org.apache.hadoop.hbase.client.ConnectionUtils.incRegionCountMetrics;
|
||||||
import static org.apache.hadoop.hbase.client.ConnectionUtils.numberOfIndividualRows;
|
import static org.apache.hadoop.hbase.client.ConnectionUtils.numberOfIndividualRows;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
@ -250,9 +251,7 @@ public abstract class ClientScanner extends AbstractClientScanner {
|
||||||
new ScannerCallableWithReplicas(getTable(), getConnection(), createScannerCallable(), pool,
|
new ScannerCallableWithReplicas(getTable(), getConnection(), createScannerCallable(), pool,
|
||||||
primaryOperationTimeout, scan, getRetries(), scannerTimeout, caching, conf, caller);
|
primaryOperationTimeout, scan, getRetries(), scannerTimeout, caching, conf, caller);
|
||||||
this.callable.setCaching(this.caching);
|
this.callable.setCaching(this.caching);
|
||||||
if (this.scanMetrics != null) {
|
incRegionCountMetrics(scanMetrics);
|
||||||
this.scanMetrics.countOfRegions.incrementAndGet();
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,7 +459,8 @@ public abstract class ClientScanner extends AbstractClientScanner {
|
||||||
// Groom the array of Results that we received back from the server before adding that
|
// Groom the array of Results that we received back from the server before adding that
|
||||||
// Results to the scanner's cache. If partial results are not allowed to be seen by the
|
// Results to the scanner's cache. If partial results are not allowed to be seen by the
|
||||||
// caller, all book keeping will be performed within this method.
|
// caller, all book keeping will be performed within this method.
|
||||||
Result[] resultsToAddToCache = scanResultCache.addAndGet(values, callable.isHeartbeatMessage());
|
Result[] resultsToAddToCache =
|
||||||
|
scanResultCache.addAndGet(values, callable.isHeartbeatMessage());
|
||||||
if (resultsToAddToCache.length > 0) {
|
if (resultsToAddToCache.length > 0) {
|
||||||
for (Result rs : resultsToAddToCache) {
|
for (Result rs : resultsToAddToCache) {
|
||||||
cache.add(rs);
|
cache.add(rs);
|
||||||
|
|
|
@ -47,16 +47,20 @@ import org.apache.hadoop.hbase.MasterNotRunningException;
|
||||||
import org.apache.hadoop.hbase.ServerName;
|
import org.apache.hadoop.hbase.ServerName;
|
||||||
import org.apache.hadoop.hbase.TableName;
|
import org.apache.hadoop.hbase.TableName;
|
||||||
import org.apache.hadoop.hbase.classification.InterfaceAudience;
|
import org.apache.hadoop.hbase.classification.InterfaceAudience;
|
||||||
|
import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
|
||||||
import org.apache.hadoop.hbase.ipc.HBaseRpcController;
|
import org.apache.hadoop.hbase.ipc.HBaseRpcController;
|
||||||
import org.apache.hadoop.hbase.security.User;
|
import org.apache.hadoop.hbase.security.User;
|
||||||
import org.apache.hadoop.hbase.security.UserProvider;
|
import org.apache.hadoop.hbase.security.UserProvider;
|
||||||
import org.apache.hadoop.hbase.shaded.com.google.protobuf.ServiceException;
|
import org.apache.hadoop.hbase.shaded.com.google.protobuf.ServiceException;
|
||||||
|
import org.apache.hadoop.hbase.shaded.protobuf.ResponseConverter;
|
||||||
import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.AdminService;
|
import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.AdminService;
|
||||||
import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ClientService;
|
import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ClientService;
|
||||||
|
import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ScanResponse;
|
||||||
import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.MasterService;
|
import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.MasterService;
|
||||||
import org.apache.hadoop.hbase.util.Bytes;
|
import org.apache.hadoop.hbase.util.Bytes;
|
||||||
import org.apache.hadoop.hbase.util.ReflectionUtils;
|
import org.apache.hadoop.hbase.util.ReflectionUtils;
|
||||||
import org.apache.hadoop.ipc.RemoteException;
|
import org.apache.hadoop.ipc.RemoteException;
|
||||||
|
import org.apache.hadoop.net.DNS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility used by client connections.
|
* Utility used by client connections.
|
||||||
|
@ -424,4 +428,75 @@ public final class ConnectionUtils {
|
||||||
return new CompleteScanResultCache();
|
return new CompleteScanResultCache();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final String MY_ADDRESS = getMyAddress();
|
||||||
|
|
||||||
|
private static String getMyAddress() {
|
||||||
|
try {
|
||||||
|
return DNS.getDefaultHost("default", "default");
|
||||||
|
} catch (UnknownHostException uhe) {
|
||||||
|
LOG.error("cannot determine my address", uhe);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isRemote(String host) {
|
||||||
|
return !host.equalsIgnoreCase(MY_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void incRPCCallsMetrics(ScanMetrics scanMetrics, boolean isRegionServerRemote) {
|
||||||
|
if (scanMetrics == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scanMetrics.countOfRPCcalls.incrementAndGet();
|
||||||
|
if (isRegionServerRemote) {
|
||||||
|
scanMetrics.countOfRemoteRPCcalls.incrementAndGet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void incRPCRetriesMetrics(ScanMetrics scanMetrics, boolean isRegionServerRemote) {
|
||||||
|
if (scanMetrics == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scanMetrics.countOfRPCRetries.incrementAndGet();
|
||||||
|
if (isRegionServerRemote) {
|
||||||
|
scanMetrics.countOfRemoteRPCRetries.incrementAndGet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void updateResultsMetrics(ScanMetrics scanMetrics, Result[] rrs,
|
||||||
|
boolean isRegionServerRemote) {
|
||||||
|
if (scanMetrics == null || rrs == null || rrs.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
long resultSize = 0;
|
||||||
|
for (Result rr : rrs) {
|
||||||
|
for (Cell cell : rr.rawCells()) {
|
||||||
|
resultSize += CellUtil.estimatedSerializedSizeOf(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scanMetrics.countOfBytesInResults.addAndGet(resultSize);
|
||||||
|
if (isRegionServerRemote) {
|
||||||
|
scanMetrics.countOfBytesInRemoteResults.addAndGet(resultSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the scan metrics returned by the server to add to the identically named counters in the
|
||||||
|
* client side metrics. If a counter does not exist with the same name as the server side metric,
|
||||||
|
* the attempt to increase the counter will fail.
|
||||||
|
*/
|
||||||
|
static void updateServerSideMetrics(ScanMetrics scanMetrics, ScanResponse response) {
|
||||||
|
if (scanMetrics == null || response == null || !response.hasScanMetrics()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ResponseConverter.getScanMetrics(response).forEach(scanMetrics::addToCounter);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void incRegionCountMetrics(ScanMetrics scanMetrics) {
|
||||||
|
if (scanMetrics == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scanMetrics.countOfRegions.incrementAndGet();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.apache.hadoop.hbase.client;
|
||||||
import org.apache.hadoop.hbase.classification.InterfaceAudience;
|
import org.apache.hadoop.hbase.classification.InterfaceAudience;
|
||||||
import org.apache.hadoop.hbase.classification.InterfaceStability;
|
import org.apache.hadoop.hbase.classification.InterfaceStability;
|
||||||
import org.apache.hadoop.hbase.client.Result;
|
import org.apache.hadoop.hbase.client.Result;
|
||||||
|
import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receives {@link Result} for an asynchronous scan.
|
* Receives {@link Result} for an asynchronous scan.
|
||||||
|
@ -112,4 +113,13 @@ public interface RawScanResultConsumer {
|
||||||
* Indicate that the scan operation is completed normally.
|
* Indicate that the scan operation is completed normally.
|
||||||
*/
|
*/
|
||||||
void onComplete();
|
void onComplete();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If {@code scan.isScanMetricsEnabled()} returns true, then this method will be called prior to
|
||||||
|
* all other methods in this interface to give you the {@link ScanMetrics} instance for this scan
|
||||||
|
* operation. The {@link ScanMetrics} instance will be updated on-the-fly during the scan, you can
|
||||||
|
* store it somewhere to get the metrics at any time if you want.
|
||||||
|
*/
|
||||||
|
default void onScanMetricsCreated(ScanMetrics scanMetrics) {
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -18,7 +18,8 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.hbase.client;
|
package org.apache.hadoop.hbase.client;
|
||||||
|
|
||||||
import static org.apache.hadoop.hbase.client.ConnectionUtils.*;
|
import static org.apache.hadoop.hbase.client.ConnectionUtils.createCloseRowBefore;
|
||||||
|
import static org.apache.hadoop.hbase.client.ConnectionUtils.incRPCRetriesMetrics;
|
||||||
import static org.apache.hadoop.hbase.client.ConnectionUtils.isEmptyStartRow;
|
import static org.apache.hadoop.hbase.client.ConnectionUtils.isEmptyStartRow;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -113,11 +114,8 @@ public class ReversedScannerCallable extends ScannerCallable {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check how often we retry.
|
// check how often we retry.
|
||||||
if (reload && this.scanMetrics != null) {
|
if (reload) {
|
||||||
this.scanMetrics.countOfRPCRetries.incrementAndGet();
|
incRPCRetriesMetrics(scanMetrics, isRegionServerRemote);
|
||||||
if (isRegionServerRemote) {
|
|
||||||
this.scanMetrics.countOfRemoteRPCRetries.incrementAndGet();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.apache.hadoop.hbase.client;
|
||||||
|
|
||||||
import org.apache.hadoop.hbase.classification.InterfaceAudience;
|
import org.apache.hadoop.hbase.classification.InterfaceAudience;
|
||||||
import org.apache.hadoop.hbase.classification.InterfaceStability;
|
import org.apache.hadoop.hbase.classification.InterfaceStability;
|
||||||
|
import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receives {@link Result} for an asynchronous scan.
|
* Receives {@link Result} for an asynchronous scan.
|
||||||
|
@ -45,4 +46,12 @@ public interface ScanResultConsumer {
|
||||||
*/
|
*/
|
||||||
void onComplete();
|
void onComplete();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If {@code scan.isScanMetricsEnabled()} returns true, then this method will be called prior to
|
||||||
|
* all other methods in this interface to give you the {@link ScanMetrics} instance for this scan
|
||||||
|
* operation. The {@link ScanMetrics} instance will be updated on-the-fly during the scan, you can
|
||||||
|
* store it somewhere to get the metrics at any time if you want.
|
||||||
|
*/
|
||||||
|
default void onScanMetricsCreated(ScanMetrics scanMetrics) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,17 +18,18 @@
|
||||||
|
|
||||||
package org.apache.hadoop.hbase.client;
|
package org.apache.hadoop.hbase.client;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.hbase.client.ConnectionUtils.incRPCCallsMetrics;
|
||||||
|
import static org.apache.hadoop.hbase.client.ConnectionUtils.incRPCRetriesMetrics;
|
||||||
|
import static org.apache.hadoop.hbase.client.ConnectionUtils.isRemote;
|
||||||
|
import static org.apache.hadoop.hbase.client.ConnectionUtils.updateResultsMetrics;
|
||||||
|
import static org.apache.hadoop.hbase.client.ConnectionUtils.updateServerSideMetrics;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InterruptedIOException;
|
import java.io.InterruptedIOException;
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.hbase.Cell;
|
|
||||||
import org.apache.hadoop.hbase.CellUtil;
|
|
||||||
import org.apache.hadoop.hbase.DoNotRetryIOException;
|
import org.apache.hadoop.hbase.DoNotRetryIOException;
|
||||||
import org.apache.hadoop.hbase.HBaseIOException;
|
import org.apache.hadoop.hbase.HBaseIOException;
|
||||||
import org.apache.hadoop.hbase.HRegionInfo;
|
import org.apache.hadoop.hbase.HRegionInfo;
|
||||||
|
@ -48,7 +49,6 @@ import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter;
|
||||||
import org.apache.hadoop.hbase.shaded.protobuf.ResponseConverter;
|
import org.apache.hadoop.hbase.shaded.protobuf.ResponseConverter;
|
||||||
import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ScanRequest;
|
import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ScanRequest;
|
||||||
import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ScanResponse;
|
import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ScanResponse;
|
||||||
import org.apache.hadoop.net.DNS;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scanner operations such as create, next, etc.
|
* Scanner operations such as create, next, etc.
|
||||||
|
@ -72,7 +72,6 @@ public class ScannerCallable extends ClientServiceCallable<Result[]> {
|
||||||
protected ScanMetrics scanMetrics;
|
protected ScanMetrics scanMetrics;
|
||||||
private boolean logScannerActivity = false;
|
private boolean logScannerActivity = false;
|
||||||
private int logCutOffLatency = 1000;
|
private int logCutOffLatency = 1000;
|
||||||
private static String myAddress;
|
|
||||||
protected final int id;
|
protected final int id;
|
||||||
|
|
||||||
enum MoreResults {
|
enum MoreResults {
|
||||||
|
@ -87,13 +86,6 @@ public class ScannerCallable extends ClientServiceCallable<Result[]> {
|
||||||
* Heartbeat messages are identified by the flag {@link ScanResponse#getHeartbeatMessage()}
|
* Heartbeat messages are identified by the flag {@link ScanResponse#getHeartbeatMessage()}
|
||||||
*/
|
*/
|
||||||
protected boolean heartbeatMessage = false;
|
protected boolean heartbeatMessage = false;
|
||||||
static {
|
|
||||||
try {
|
|
||||||
myAddress = DNS.getDefaultHost("default", "default");
|
|
||||||
} catch (UnknownHostException uhe) {
|
|
||||||
LOG.error("cannot determine my address", uhe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// indicate if it is a remote server call
|
// indicate if it is a remote server call
|
||||||
protected boolean isRegionServerRemote = true;
|
protected boolean isRegionServerRemote = true;
|
||||||
|
@ -158,30 +150,23 @@ public class ScannerCallable extends ClientServiceCallable<Result[]> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check how often we retry.
|
// check how often we retry.
|
||||||
if (reload && this.scanMetrics != null) {
|
if (reload) {
|
||||||
this.scanMetrics.countOfRPCRetries.incrementAndGet();
|
incRPCRetriesMetrics(scanMetrics, isRegionServerRemote);
|
||||||
if (isRegionServerRemote) {
|
|
||||||
this.scanMetrics.countOfRemoteRPCRetries.incrementAndGet();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* compare the local machine hostname with region server's hostname
|
* compare the local machine hostname with region server's hostname to decide if hbase client
|
||||||
* to decide if hbase client connects to a remote region server
|
* connects to a remote region server
|
||||||
*/
|
*/
|
||||||
protected void checkIfRegionServerIsRemote() {
|
protected void checkIfRegionServerIsRemote() {
|
||||||
if (getLocation().getHostname().equalsIgnoreCase(myAddress)) {
|
isRegionServerRemote = isRemote(getLocation().getHostname());
|
||||||
isRegionServerRemote = false;
|
|
||||||
} else {
|
|
||||||
isRegionServerRemote = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScanResponse next() throws IOException {
|
private ScanResponse next() throws IOException {
|
||||||
// Reset the heartbeat flag prior to each RPC in case an exception is thrown by the server
|
// Reset the heartbeat flag prior to each RPC in case an exception is thrown by the server
|
||||||
setHeartbeatMessage(false);
|
setHeartbeatMessage(false);
|
||||||
incRPCcallsMetrics();
|
incRPCCallsMetrics(scanMetrics, isRegionServerRemote);
|
||||||
ScanRequest request = RequestConverter.buildScanRequest(scannerId, caching, false, nextCallSeq,
|
ScanRequest request = RequestConverter.buildScanRequest(scannerId, caching, false, nextCallSeq,
|
||||||
this.scanMetrics != null, renew, scan.getLimit());
|
this.scanMetrics != null, renew, scan.getLimit());
|
||||||
try {
|
try {
|
||||||
|
@ -267,7 +252,7 @@ public class ScannerCallable extends ClientServiceCallable<Result[]> {
|
||||||
+ scannerId);
|
+ scannerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateServerSideMetrics(response);
|
updateServerSideMetrics(scanMetrics, response);
|
||||||
// moreResults is only used for the case where a filter exhausts all elements
|
// moreResults is only used for the case where a filter exhausts all elements
|
||||||
if (response.hasMoreResults()) {
|
if (response.hasMoreResults()) {
|
||||||
if (response.getMoreResults()) {
|
if (response.getMoreResults()) {
|
||||||
|
@ -289,7 +274,7 @@ public class ScannerCallable extends ClientServiceCallable<Result[]> {
|
||||||
} else {
|
} else {
|
||||||
setMoreResultsInRegion(MoreResults.UNKNOWN);
|
setMoreResultsInRegion(MoreResults.UNKNOWN);
|
||||||
}
|
}
|
||||||
updateResultsMetrics(rrs);
|
updateResultsMetrics(scanMetrics, rrs, isRegionServerRemote);
|
||||||
return rrs;
|
return rrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,53 +292,12 @@ public class ScannerCallable extends ClientServiceCallable<Result[]> {
|
||||||
this.heartbeatMessage = heartbeatMessage;
|
this.heartbeatMessage = heartbeatMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void incRPCcallsMetrics() {
|
|
||||||
if (this.scanMetrics == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.scanMetrics.countOfRPCcalls.incrementAndGet();
|
|
||||||
if (isRegionServerRemote) {
|
|
||||||
this.scanMetrics.countOfRemoteRPCcalls.incrementAndGet();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void updateResultsMetrics(Result[] rrs) {
|
|
||||||
if (this.scanMetrics == null || rrs == null || rrs.length == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
long resultSize = 0;
|
|
||||||
for (Result rr : rrs) {
|
|
||||||
for (Cell cell : rr.rawCells()) {
|
|
||||||
resultSize += CellUtil.estimatedSerializedSizeOf(cell);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.scanMetrics.countOfBytesInResults.addAndGet(resultSize);
|
|
||||||
if (isRegionServerRemote) {
|
|
||||||
this.scanMetrics.countOfBytesInRemoteResults.addAndGet(resultSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use the scan metrics returned by the server to add to the identically named counters in the
|
|
||||||
* client side metrics. If a counter does not exist with the same name as the server side metric,
|
|
||||||
* the attempt to increase the counter will fail.
|
|
||||||
* @param response
|
|
||||||
*/
|
|
||||||
private void updateServerSideMetrics(ScanResponse response) {
|
|
||||||
if (this.scanMetrics == null || response == null || !response.hasScanMetrics()) return;
|
|
||||||
|
|
||||||
Map<String, Long> serverMetrics = ResponseConverter.getScanMetrics(response);
|
|
||||||
for (Entry<String, Long> entry : serverMetrics.entrySet()) {
|
|
||||||
this.scanMetrics.addToCounter(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void close() {
|
private void close() {
|
||||||
if (this.scannerId == -1L) {
|
if (this.scannerId == -1L) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
incRPCcallsMetrics();
|
incRPCCallsMetrics(scanMetrics, isRegionServerRemote);
|
||||||
ScanRequest request =
|
ScanRequest request =
|
||||||
RequestConverter.buildScanRequest(this.scannerId, 0, true, this.scanMetrics != null);
|
RequestConverter.buildScanRequest(this.scannerId, 0, true, this.scanMetrics != null);
|
||||||
try {
|
try {
|
||||||
|
@ -371,7 +315,7 @@ public class ScannerCallable extends ClientServiceCallable<Result[]> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScanResponse openScanner() throws IOException {
|
private ScanResponse openScanner() throws IOException {
|
||||||
incRPCcallsMetrics();
|
incRPCCallsMetrics(scanMetrics, isRegionServerRemote);
|
||||||
ScanRequest request = RequestConverter.buildScanRequest(
|
ScanRequest request = RequestConverter.buildScanRequest(
|
||||||
getLocation().getRegionInfo().getRegionName(), this.scan, this.caching, false);
|
getLocation().getRegionInfo().getRegionName(), this.scan, this.caching, false);
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
/**
|
||||||
|
* 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 com.google.common.base.Throwables;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
|
import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
|
||||||
|
|
||||||
|
class SimpleRawScanResultConsumer implements RawScanResultConsumer {
|
||||||
|
|
||||||
|
private ScanMetrics scanMetrics;
|
||||||
|
|
||||||
|
private final Queue<Result> queue = new ArrayDeque<>();
|
||||||
|
|
||||||
|
private boolean finished;
|
||||||
|
|
||||||
|
private Throwable error;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScanMetricsCreated(ScanMetrics scanMetrics) {
|
||||||
|
this.scanMetrics = scanMetrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void onNext(Result[] results, ScanController controller) {
|
||||||
|
for (Result result : results) {
|
||||||
|
queue.offer(result);
|
||||||
|
}
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void onError(Throwable error) {
|
||||||
|
finished = true;
|
||||||
|
this.error = error;
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void onComplete() {
|
||||||
|
finished = true;
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized Result take() throws IOException, InterruptedException {
|
||||||
|
for (;;) {
|
||||||
|
if (!queue.isEmpty()) {
|
||||||
|
return queue.poll();
|
||||||
|
}
|
||||||
|
if (finished) {
|
||||||
|
if (error != null) {
|
||||||
|
Throwables.propagateIfPossible(error, IOException.class);
|
||||||
|
throw new IOException(error);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScanMetrics getScanMetrics() {
|
||||||
|
return scanMetrics;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
/**
|
||||||
|
* 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 com.google.common.base.Throwables;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
|
||||||
|
|
||||||
|
final class SimpleScanResultConsumer implements ScanResultConsumer {
|
||||||
|
|
||||||
|
private ScanMetrics scanMetrics;
|
||||||
|
|
||||||
|
private final List<Result> results = new ArrayList<>();
|
||||||
|
|
||||||
|
private Throwable error;
|
||||||
|
|
||||||
|
private boolean finished = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScanMetricsCreated(ScanMetrics scanMetrics) {
|
||||||
|
this.scanMetrics = scanMetrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean onNext(Result result) {
|
||||||
|
results.add(result);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void onError(Throwable error) {
|
||||||
|
this.error = error;
|
||||||
|
finished = true;
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void onComplete() {
|
||||||
|
finished = true;
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized List<Result> getAll() throws Exception {
|
||||||
|
while (!finished) {
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
if (error != null) {
|
||||||
|
Throwables.propagateIfPossible(error, Exception.class);
|
||||||
|
throw new Exception(error);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScanMetrics getScanMetrics() {
|
||||||
|
return scanMetrics;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,9 +17,6 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.hbase.client;
|
package org.apache.hadoop.hbase.client;
|
||||||
|
|
||||||
import com.google.common.base.Throwables;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ForkJoinPool;
|
import java.util.concurrent.ForkJoinPool;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
@ -37,45 +34,6 @@ import org.junit.runners.Parameterized.Parameters;
|
||||||
@Category({ LargeTests.class, ClientTests.class })
|
@Category({ LargeTests.class, ClientTests.class })
|
||||||
public class TestAsyncTableScan extends AbstractTestAsyncTableScan {
|
public class TestAsyncTableScan extends AbstractTestAsyncTableScan {
|
||||||
|
|
||||||
private static final class SimpleScanResultConsumer implements ScanResultConsumer {
|
|
||||||
|
|
||||||
private final List<Result> results = new ArrayList<>();
|
|
||||||
|
|
||||||
private Throwable error;
|
|
||||||
|
|
||||||
private boolean finished = false;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized boolean onNext(Result result) {
|
|
||||||
results.add(result);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void onError(Throwable error) {
|
|
||||||
this.error = error;
|
|
||||||
finished = true;
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void onComplete() {
|
|
||||||
finished = true;
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized List<Result> getAll() throws Exception {
|
|
||||||
while (!finished) {
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
if (error != null) {
|
|
||||||
Throwables.propagateIfPossible(error, Exception.class);
|
|
||||||
throw new Exception(error);
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Parameter(0)
|
@Parameter(0)
|
||||||
public String scanType;
|
public String scanType;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,159 @@
|
||||||
|
/**
|
||||||
|
* 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 static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ForkJoinPool;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.hadoop.hbase.CellUtil;
|
||||||
|
import org.apache.hadoop.hbase.HBaseTestingUtility;
|
||||||
|
import org.apache.hadoop.hbase.TableName;
|
||||||
|
import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.ClientTests;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.MediumTests;
|
||||||
|
import org.apache.hadoop.hbase.util.Bytes;
|
||||||
|
import org.apache.hadoop.hbase.util.Pair;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
import org.junit.runners.Parameterized.Parameter;
|
||||||
|
import org.junit.runners.Parameterized.Parameters;
|
||||||
|
|
||||||
|
@RunWith(Parameterized.class)
|
||||||
|
@Category({ MediumTests.class, ClientTests.class })
|
||||||
|
public class TestAsyncTableScanMetrics {
|
||||||
|
|
||||||
|
private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
|
||||||
|
|
||||||
|
private static final TableName TABLE_NAME = TableName.valueOf("ScanMetrics");
|
||||||
|
|
||||||
|
private static final byte[] CF = Bytes.toBytes("cf");
|
||||||
|
|
||||||
|
private static final byte[] CQ = Bytes.toBytes("cq");
|
||||||
|
|
||||||
|
private static final byte[] VALUE = Bytes.toBytes("value");
|
||||||
|
|
||||||
|
private static AsyncConnection CONN;
|
||||||
|
|
||||||
|
private static int NUM_REGIONS;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface ScanWithMetrics {
|
||||||
|
Pair<List<Result>, ScanMetrics> scan(Scan scan) throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameter(0)
|
||||||
|
public String methodName;
|
||||||
|
|
||||||
|
@Parameter(1)
|
||||||
|
public ScanWithMetrics method;
|
||||||
|
|
||||||
|
@Parameters(name = "{index}: scan={0}")
|
||||||
|
public static List<Object[]> params() {
|
||||||
|
ScanWithMetrics doScanWithRawAsyncTable = TestAsyncTableScanMetrics::doScanWithRawAsyncTable;
|
||||||
|
ScanWithMetrics doScanWithAsyncTableScan = TestAsyncTableScanMetrics::doScanWithAsyncTableScan;
|
||||||
|
ScanWithMetrics doScanWithAsyncTableScanner =
|
||||||
|
TestAsyncTableScanMetrics::doScanWithAsyncTableScanner;
|
||||||
|
return Arrays.asList(new Object[] { "doScanWithRawAsyncTable", doScanWithRawAsyncTable },
|
||||||
|
new Object[] { "doScanWithAsyncTableScan", doScanWithAsyncTableScan },
|
||||||
|
new Object[] { "doScanWithAsyncTableScanner", doScanWithAsyncTableScanner });
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setUp() throws Exception {
|
||||||
|
UTIL.startMiniCluster(3);
|
||||||
|
// Create 3 rows in the table, with rowkeys starting with "zzz*" so that
|
||||||
|
// scan are forced to hit all the regions.
|
||||||
|
try (Table table = UTIL.createMultiRegionTable(TABLE_NAME, CF)) {
|
||||||
|
table.put(Arrays.asList(new Put(Bytes.toBytes("zzz1")).addColumn(CF, CQ, VALUE),
|
||||||
|
new Put(Bytes.toBytes("zzz2")).addColumn(CF, CQ, VALUE),
|
||||||
|
new Put(Bytes.toBytes("zzz3")).addColumn(CF, CQ, VALUE)));
|
||||||
|
}
|
||||||
|
CONN = ConnectionFactory.createAsyncConnection(UTIL.getConfiguration()).get();
|
||||||
|
NUM_REGIONS = UTIL.getHBaseCluster().getRegions(TABLE_NAME).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void tearDown() throws Exception {
|
||||||
|
IOUtils.closeQuietly(CONN);
|
||||||
|
UTIL.shutdownMiniCluster();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Pair<List<Result>, ScanMetrics> doScanWithRawAsyncTable(Scan scan)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
|
SimpleRawScanResultConsumer consumer = new SimpleRawScanResultConsumer();
|
||||||
|
CONN.getRawTable(TABLE_NAME).scan(scan, consumer);
|
||||||
|
List<Result> results = new ArrayList<>();
|
||||||
|
for (Result result; (result = consumer.take()) != null;) {
|
||||||
|
results.add(result);
|
||||||
|
}
|
||||||
|
return Pair.newPair(results, consumer.getScanMetrics());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Pair<List<Result>, ScanMetrics> doScanWithAsyncTableScan(Scan scan)
|
||||||
|
throws Exception {
|
||||||
|
SimpleScanResultConsumer consumer = new SimpleScanResultConsumer();
|
||||||
|
CONN.getTable(TABLE_NAME, ForkJoinPool.commonPool()).scan(scan, consumer);
|
||||||
|
return Pair.newPair(consumer.getAll(), consumer.getScanMetrics());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Pair<List<Result>, ScanMetrics> doScanWithAsyncTableScanner(Scan scan)
|
||||||
|
throws IOException {
|
||||||
|
try (ResultScanner scanner =
|
||||||
|
CONN.getTable(TABLE_NAME, ForkJoinPool.commonPool()).getScanner(scan)) {
|
||||||
|
List<Result> results = new ArrayList<>();
|
||||||
|
for (Result result; (result = scanner.next()) != null;) {
|
||||||
|
results.add(result);
|
||||||
|
}
|
||||||
|
return Pair.newPair(results, scanner.getScanMetrics());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoScanMetrics() throws Exception {
|
||||||
|
Pair<List<Result>, ScanMetrics> pair = method.scan(new Scan());
|
||||||
|
assertEquals(3, pair.getFirst().size());
|
||||||
|
assertNull(pair.getSecond());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testScanMetrics() throws Exception {
|
||||||
|
Pair<List<Result>, ScanMetrics> pair = method.scan(new Scan().setScanMetricsEnabled(true));
|
||||||
|
List<Result> results = pair.getFirst();
|
||||||
|
assertEquals(3, results.size());
|
||||||
|
long bytes = results.stream().flatMap(r -> Arrays.asList(r.rawCells()).stream())
|
||||||
|
.mapToLong(c -> CellUtil.estimatedSerializedSizeOf(c)).sum();
|
||||||
|
ScanMetrics scanMetrics = pair.getSecond();
|
||||||
|
assertEquals(NUM_REGIONS, scanMetrics.countOfRegions.get());
|
||||||
|
assertEquals(bytes, scanMetrics.countOfBytesInResults.get());
|
||||||
|
assertEquals(NUM_REGIONS, scanMetrics.countOfRPCcalls.get());
|
||||||
|
// also assert a server side metric to ensure that we have published them into the client side
|
||||||
|
// metrics.
|
||||||
|
assertEquals(3, scanMetrics.countOfRowsScanned.get());
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,13 +17,8 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.hbase.client;
|
package org.apache.hadoop.hbase.client;
|
||||||
|
|
||||||
import com.google.common.base.Throwables;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayDeque;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ -39,53 +34,6 @@ import org.junit.runners.Parameterized.Parameters;
|
||||||
@Category({ MediumTests.class, ClientTests.class })
|
@Category({ MediumTests.class, ClientTests.class })
|
||||||
public class TestRawAsyncTableScan extends AbstractTestAsyncTableScan {
|
public class TestRawAsyncTableScan extends AbstractTestAsyncTableScan {
|
||||||
|
|
||||||
private static final class SimpleRawScanResultConsumer implements RawScanResultConsumer {
|
|
||||||
|
|
||||||
private final Queue<Result> queue = new ArrayDeque<>();
|
|
||||||
|
|
||||||
private boolean finished;
|
|
||||||
|
|
||||||
private Throwable error;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void onNext(Result[] results, ScanController controller) {
|
|
||||||
for (Result result : results) {
|
|
||||||
queue.offer(result);
|
|
||||||
}
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void onError(Throwable error) {
|
|
||||||
finished = true;
|
|
||||||
this.error = error;
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void onComplete() {
|
|
||||||
finished = true;
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized Result take() throws IOException, InterruptedException {
|
|
||||||
for (;;) {
|
|
||||||
if (!queue.isEmpty()) {
|
|
||||||
return queue.poll();
|
|
||||||
}
|
|
||||||
if (finished) {
|
|
||||||
if (error != null) {
|
|
||||||
Throwables.propagateIfPossible(error, IOException.class);
|
|
||||||
throw new IOException(error);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Parameter(0)
|
@Parameter(0)
|
||||||
public String scanType;
|
public String scanType;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue