Skip to content

Commit 878342a

Browse files
sanjeet006pySanjeet Malhotra
authored andcommitted
HBASE-29233: Capture scan metrics at region level (apache#7132) (apache#6868)
Signed-off-by: Viraj Jasani <[email protected]>
1 parent 035ca32 commit 878342a

25 files changed

+1742
-54
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ linklint/
2323
.java-version
2424
tmp
2525
**/.flattened-pom.xml
26+
.vscode/

hbase-client/src/main/java/org/apache/hadoop/hbase/client/AbstractClientScanner.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
@InterfaceAudience.Private
2727
public abstract class AbstractClientScanner implements ResultScanner {
2828
protected ScanMetrics scanMetrics;
29+
private boolean isScanMetricsByRegionEnabled = false;
2930

3031
/**
3132
* Check and initialize if application wants to collect scan metrics
@@ -34,6 +35,9 @@ protected void initScanMetrics(Scan scan) {
3435
// check if application wants to collect scan metrics
3536
if (scan.isScanMetricsEnabled()) {
3637
scanMetrics = new ScanMetrics();
38+
if (scan.isScanMetricsByRegionEnabled()) {
39+
isScanMetricsByRegionEnabled = true;
40+
}
3741
}
3842
}
3943

@@ -46,4 +50,12 @@ protected void initScanMetrics(Scan scan) {
4650
public ScanMetrics getScanMetrics() {
4751
return scanMetrics;
4852
}
53+
54+
protected void setIsScanMetricsByRegionEnabled(boolean isScanMetricsByRegionEnabled) {
55+
this.isScanMetricsByRegionEnabled = isScanMetricsByRegionEnabled;
56+
}
57+
58+
protected boolean isScanMetricsByRegionEnabled() {
59+
return isScanMetricsByRegionEnabled;
60+
}
4961
}

hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncClientScanner.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ class AsyncClientScanner {
9292

9393
private final Span span;
9494

95+
private final boolean isScanMetricsByRegionEnabled;
96+
9597
public AsyncClientScanner(Scan scan, AdvancedScanResultConsumer consumer, TableName tableName,
9698
AsyncConnectionImpl conn, Timer retryTimer, long pauseNs, long pauseNsForServerOverloaded,
9799
int maxAttempts, long scanTimeoutNs, long rpcTimeoutNs, int startLogErrorsCnt) {
@@ -113,12 +115,17 @@ public AsyncClientScanner(Scan scan, AdvancedScanResultConsumer consumer, TableN
113115
this.rpcTimeoutNs = rpcTimeoutNs;
114116
this.startLogErrorsCnt = startLogErrorsCnt;
115117
this.resultCache = createScanResultCache(scan);
118+
boolean isScanMetricsByRegionEnabled = false;
116119
if (scan.isScanMetricsEnabled()) {
117120
this.scanMetrics = new ScanMetrics();
118121
consumer.onScanMetricsCreated(scanMetrics);
122+
if (this.scan.isScanMetricsByRegionEnabled()) {
123+
isScanMetricsByRegionEnabled = true;
124+
}
119125
} else {
120126
this.scanMetrics = null;
121127
}
128+
this.isScanMetricsByRegionEnabled = isScanMetricsByRegionEnabled;
122129

123130
/*
124131
* Assumes that the `start()` method is called immediately after construction. If this is no
@@ -243,6 +250,9 @@ private long getPrimaryTimeoutNs() {
243250
}
244251

245252
private void openScanner() {
253+
if (this.isScanMetricsByRegionEnabled) {
254+
scanMetrics.moveToNextRegion();
255+
}
246256
incRegionCountMetrics(scanMetrics);
247257
openScannerTries.set(1);
248258
addListener(timelineConsistentRead(conn.getLocator(), tableName, scan, scan.getStartRow(),
@@ -258,6 +268,11 @@ private void openScanner() {
258268
span.end();
259269
}
260270
}
271+
if (this.isScanMetricsByRegionEnabled) {
272+
HRegionLocation loc = resp.loc;
273+
this.scanMetrics.initScanMetricsRegionInfo(loc.getRegion().getEncodedName(),
274+
loc.getServerName());
275+
}
261276
startScan(resp);
262277
}
263278
});

hbase-client/src/main/java/org/apache/hadoop/hbase/client/ClientScanner.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@
3434
import org.apache.hadoop.hbase.DoNotRetryIOException;
3535
import org.apache.hadoop.hbase.HConstants;
3636
import org.apache.hadoop.hbase.HRegionInfo;
37+
import org.apache.hadoop.hbase.HRegionLocation;
3738
import org.apache.hadoop.hbase.NotServingRegionException;
3839
import org.apache.hadoop.hbase.TableName;
3940
import org.apache.hadoop.hbase.UnknownScannerException;
4041
import org.apache.hadoop.hbase.client.ScannerCallable.MoreResults;
42+
import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
4143
import org.apache.hadoop.hbase.exceptions.OutOfOrderScannerNextException;
4244
import org.apache.hadoop.hbase.exceptions.ScannerResetException;
4345
import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
@@ -124,6 +126,10 @@ public ClientScanner(final Configuration conf, final Scan scan, final TableName
124126
this.readRpcTimeout = scanReadRpcTimeout;
125127
this.scannerTimeout = scannerTimeout;
126128

129+
if (scan.isScanMetricsByRegionEnabled() && scan.getConsistency() == Consistency.TIMELINE) {
130+
scan.setEnableScanMetricsByRegion(false);
131+
LOG.warn("Scan metrics by region is not supported for timeline consistency in HBase 2");
132+
}
127133
// check if application wants to collect scan metrics
128134
initScanMetrics(scan);
129135

@@ -249,6 +255,9 @@ protected boolean moveToNextRegion() {
249255
// clear the current region, we will set a new value to it after the first call of the new
250256
// callable.
251257
this.currentRegion = null;
258+
if (isScanMetricsByRegionEnabled()) {
259+
scanMetrics.moveToNextRegion();
260+
}
252261
this.callable = new ScannerCallableWithReplicas(getTable(), getConnection(),
253262
createScannerCallable(), pool, primaryOperationTimeout, scan, getRetries(), readRpcTimeout,
254263
scannerTimeout, caching, conf, caller);
@@ -271,6 +280,7 @@ private Result[] call(ScannerCallableWithReplicas callable, RpcRetryingCaller<Re
271280
Result[] rrs = caller.callWithoutRetries(callable, scannerTimeout);
272281
if (currentRegion == null && updateCurrentRegion) {
273282
currentRegion = callable.getHRegionInfo();
283+
initScanMetricsRegionInfo();
274284
}
275285
return rrs;
276286
}
@@ -459,7 +469,8 @@ protected void loadCache() throws IOException {
459469
}
460470
long currentTime = EnvironmentEdgeManager.currentTime();
461471
if (this.scanMetrics != null) {
462-
this.scanMetrics.sumOfMillisSecBetweenNexts.addAndGet(currentTime - lastNext);
472+
this.scanMetrics.addToCounter(ScanMetrics.MILLIS_BETWEEN_NEXTS_METRIC_NAME,
473+
currentTime - lastNext);
463474
}
464475
lastNext = currentTime;
465476
// Groom the array of Results that we received back from the server before adding that
@@ -612,4 +623,12 @@ public Result next() throws IOException {
612623
return nextWithSyncCache();
613624
}
614625
}
626+
627+
private void initScanMetricsRegionInfo() {
628+
if (isScanMetricsByRegionEnabled()) {
629+
HRegionLocation location = callable.getLocation();
630+
String encodedRegionName = location.getRegion().getEncodedName();
631+
scanMetrics.initScanMetricsRegionInfo(encodedRegionName, location.getServerName());
632+
}
633+
}
615634
}

hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionUtils.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -360,19 +360,19 @@ static void incRPCCallsMetrics(ScanMetrics scanMetrics, boolean isRegionServerRe
360360
if (scanMetrics == null) {
361361
return;
362362
}
363-
scanMetrics.countOfRPCcalls.incrementAndGet();
363+
scanMetrics.addToCounter(ScanMetrics.RPC_CALLS_METRIC_NAME, 1);
364364
if (isRegionServerRemote) {
365-
scanMetrics.countOfRemoteRPCcalls.incrementAndGet();
365+
scanMetrics.addToCounter(ScanMetrics.REMOTE_RPC_CALLS_METRIC_NAME, 1);
366366
}
367367
}
368368

369369
static void incRPCRetriesMetrics(ScanMetrics scanMetrics, boolean isRegionServerRemote) {
370370
if (scanMetrics == null) {
371371
return;
372372
}
373-
scanMetrics.countOfRPCRetries.incrementAndGet();
373+
scanMetrics.addToCounter(ScanMetrics.RPC_RETRIES_METRIC_NAME, 1);
374374
if (isRegionServerRemote) {
375-
scanMetrics.countOfRemoteRPCRetries.incrementAndGet();
375+
scanMetrics.addToCounter(ScanMetrics.REMOTE_RPC_RETRIES_METRIC_NAME, 1);
376376
}
377377
}
378378

@@ -387,9 +387,9 @@ static void updateResultsMetrics(ScanMetrics scanMetrics, Result[] rrs,
387387
resultSize += PrivateCellUtil.estimatedSerializedSizeOf(cell);
388388
}
389389
}
390-
scanMetrics.countOfBytesInResults.addAndGet(resultSize);
390+
scanMetrics.addToCounter(ScanMetrics.BYTES_IN_RESULTS_METRIC_NAME, resultSize);
391391
if (isRegionServerRemote) {
392-
scanMetrics.countOfBytesInRemoteResults.addAndGet(resultSize);
392+
scanMetrics.addToCounter(ScanMetrics.BYTES_IN_REMOTE_RESULTS_METRIC_NAME, resultSize);
393393
}
394394
}
395395

@@ -409,7 +409,7 @@ static void incRegionCountMetrics(ScanMetrics scanMetrics) {
409409
if (scanMetrics == null) {
410410
return;
411411
}
412-
scanMetrics.countOfRegions.incrementAndGet();
412+
scanMetrics.addToCounter(ScanMetrics.REGIONS_SCANNED_METRIC_NAME, 1);
413413
}
414414

415415
/**

hbase-client/src/main/java/org/apache/hadoop/hbase/client/ImmutableScan.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,12 @@ public Scan setScanMetricsEnabled(final boolean enabled) {
240240
"ImmutableScan does not allow access to setScanMetricsEnabled");
241241
}
242242

243+
@Override
244+
public Scan setEnableScanMetricsByRegion(final boolean enable) {
245+
throw new UnsupportedOperationException(
246+
"ImmutableScan does not allow access to setEnableScanMetricsByRegion");
247+
}
248+
243249
@Override
244250
@Deprecated
245251
public Scan setAsyncPrefetch(boolean asyncPrefetch) {
@@ -420,6 +426,11 @@ public boolean isScanMetricsEnabled() {
420426
return this.delegateScan.isScanMetricsEnabled();
421427
}
422428

429+
@Override
430+
public boolean isScanMetricsByRegionEnabled() {
431+
return this.delegateScan.isScanMetricsByRegionEnabled();
432+
}
433+
423434
@Override
424435
public Boolean isAsyncPrefetch() {
425436
return this.delegateScan.isAsyncPrefetch();

hbase-client/src/main/java/org/apache/hadoop/hbase/client/Scan.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ public class Scan extends Query {
130130
// define this attribute with the appropriate table name by calling
131131
// scan.setAttribute(Scan.SCAN_ATTRIBUTES_TABLE_NAME, Bytes.toBytes(tableName))
132132
static public final String SCAN_ATTRIBUTES_TABLE_NAME = "scan.attributes.table.name";
133+
static private final String SCAN_ATTRIBUTES_METRICS_BY_REGION_ENABLE =
134+
"scan.attributes.metrics.byregion.enable";
133135

134136
/**
135137
* -1 means no caching specified and the value of {@link HConstants#HBASE_CLIENT_SCANNER_CACHING}
@@ -1099,11 +1101,15 @@ public Scan setPriority(int priority) {
10991101
}
11001102

11011103
/**
1102-
* Enable collection of {@link ScanMetrics}. For advanced users.
1104+
* Enable collection of {@link ScanMetrics}. For advanced users. While disabling scan metrics,
1105+
* will also disable region level scan metrics.
11031106
* @param enabled Set to true to enable accumulating scan metrics
11041107
*/
11051108
public Scan setScanMetricsEnabled(final boolean enabled) {
11061109
setAttribute(Scan.SCAN_ATTRIBUTES_METRICS_ENABLE, Bytes.toBytes(Boolean.valueOf(enabled)));
1110+
if (!enabled) {
1111+
setEnableScanMetricsByRegion(false);
1112+
}
11071113
return this;
11081114
}
11091115

@@ -1236,4 +1242,22 @@ public boolean isNeedCursorResult() {
12361242
public static Scan createScanFromCursor(Cursor cursor) {
12371243
return new Scan().withStartRow(cursor.getRow());
12381244
}
1245+
1246+
/**
1247+
* Enables region level scan metrics. If scan metrics are disabled then first enables scan metrics
1248+
* followed by region level scan metrics.
1249+
* @param enable Set to true to enable region level scan metrics.
1250+
*/
1251+
public Scan setEnableScanMetricsByRegion(final boolean enable) {
1252+
if (enable) {
1253+
setScanMetricsEnabled(true);
1254+
}
1255+
setAttribute(Scan.SCAN_ATTRIBUTES_METRICS_BY_REGION_ENABLE, Bytes.toBytes(enable));
1256+
return this;
1257+
}
1258+
1259+
public boolean isScanMetricsByRegionEnabled() {
1260+
byte[] attr = getAttribute(Scan.SCAN_ATTRIBUTES_METRICS_BY_REGION_ENABLE);
1261+
return attr != null && Bytes.toBoolean(attr);
1262+
}
12391263
}

hbase-client/src/main/java/org/apache/hadoop/hbase/client/ScannerCallable.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ private ScanResponse next() throws IOException {
226226
// when what we need is to open scanner against new location.
227227
// Attach NSRE to signal client that it needs to re-setup scanner.
228228
if (this.scanMetrics != null) {
229-
this.scanMetrics.countOfNSRE.incrementAndGet();
229+
this.scanMetrics.addToCounter(ScanMetrics.NOT_SERVING_REGION_EXCEPTION_METRIC_NAME, 1);
230230
}
231231
throw new DoNotRetryIOException("Resetting the scanner -- see exception cause", ioe);
232232
} else if (ioe instanceof RegionServerStoppedException) {

hbase-client/src/main/java/org/apache/hadoop/hbase/client/ScannerCallableWithReplicas.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.apache.hadoop.conf.Configuration;
3232
import org.apache.hadoop.hbase.DoNotRetryIOException;
3333
import org.apache.hadoop.hbase.HRegionInfo;
34+
import org.apache.hadoop.hbase.HRegionLocation;
3435
import org.apache.hadoop.hbase.RegionLocations;
3536
import org.apache.hadoop.hbase.TableName;
3637
import org.apache.hadoop.hbase.client.ScannerCallable.MoreResults;
@@ -467,4 +468,8 @@ public String getExceptionMessageAdditionalDetail() {
467468
public long sleep(long pause, int tries) {
468469
return currentScannerCallable.sleep(pause, tries);
469470
}
471+
472+
public HRegionLocation getLocation() {
473+
return currentScannerCallable.getLocation();
474+
}
470475
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.hbase.client.metrics;
19+
20+
import java.util.HashMap;
21+
import java.util.Map;
22+
import java.util.concurrent.atomic.AtomicLong;
23+
import org.apache.hadoop.hbase.ServerName;
24+
import org.apache.yetus.audience.InterfaceAudience;
25+
26+
/**
27+
* Captures region level scan metrics as a map of metric name ({@link String}) -> Value
28+
* ({@link AtomicLong}). <br/>
29+
* <br/>
30+
* One instance stores scan metrics for a single region only.
31+
*/
32+
@InterfaceAudience.Private
33+
public class RegionScanMetricsData {
34+
private final Map<String, AtomicLong> counters = new HashMap<>();
35+
private ScanMetricsRegionInfo scanMetricsRegionInfo =
36+
ScanMetricsRegionInfo.EMPTY_SCAN_METRICS_REGION_INFO;
37+
38+
AtomicLong createCounter(String counterName) {
39+
return ScanMetricsUtil.createCounter(counters, counterName);
40+
}
41+
42+
void setCounter(String counterName, long value) {
43+
ScanMetricsUtil.setCounter(counters, counterName, value);
44+
}
45+
46+
void addToCounter(String counterName, long delta) {
47+
ScanMetricsUtil.addToCounter(counters, counterName, delta);
48+
}
49+
50+
Map<String, Long> collectMetrics(boolean reset) {
51+
return ScanMetricsUtil.collectMetrics(counters, reset);
52+
}
53+
54+
@Override
55+
public String toString() {
56+
return getClass().getSimpleName() + "[" + scanMetricsRegionInfo + "," + "Counters=" + counters
57+
+ "]";
58+
}
59+
60+
/**
61+
* Populate encoded region name and server name details if not already populated. If details are
62+
* already populated and a re-attempt is done then {@link UnsupportedOperationException} is
63+
* thrown.
64+
*/
65+
void initScanMetricsRegionInfo(String encodedRegionName, ServerName serverName) {
66+
// Check by reference
67+
if (scanMetricsRegionInfo == ScanMetricsRegionInfo.EMPTY_SCAN_METRICS_REGION_INFO) {
68+
scanMetricsRegionInfo = new ScanMetricsRegionInfo(encodedRegionName, serverName);
69+
} else {
70+
throw new UnsupportedOperationException(
71+
"ScanMetricsRegionInfo has already been initialized to " + scanMetricsRegionInfo
72+
+ " and cannot be re-initialized to region: " + encodedRegionName + " and server: "
73+
+ serverName);
74+
}
75+
}
76+
77+
ScanMetricsRegionInfo getScanMetricsRegionInfo() {
78+
return scanMetricsRegionInfo;
79+
}
80+
}

0 commit comments

Comments
 (0)