Skip to content

Commit a15f555

Browse files
sanjeet006pymokai87
authored andcommitted
HBASE-29233: Capture scan metrics at region level (apache#7132) (apache#6868)
Signed-off-by: Viraj Jasani <[email protected]>
1 parent 0e06e4d commit a15f555

25 files changed

+1750
-57
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
@@ -95,6 +95,8 @@ class AsyncClientScanner {
9595

9696
private final Map<String, byte[]> requestAttributes;
9797

98+
private final boolean isScanMetricsByRegionEnabled;
99+
98100
public AsyncClientScanner(Scan scan, AdvancedScanResultConsumer consumer, TableName tableName,
99101
AsyncConnectionImpl conn, Timer retryTimer, long pauseNs, long pauseNsForServerOverloaded,
100102
int maxAttempts, long scanTimeoutNs, long rpcTimeoutNs, int startLogErrorsCnt,
@@ -118,12 +120,17 @@ public AsyncClientScanner(Scan scan, AdvancedScanResultConsumer consumer, TableN
118120
this.startLogErrorsCnt = startLogErrorsCnt;
119121
this.resultCache = createScanResultCache(scan);
120122
this.requestAttributes = requestAttributes;
123+
boolean isScanMetricsByRegionEnabled = false;
121124
if (scan.isScanMetricsEnabled()) {
122125
this.scanMetrics = new ScanMetrics();
123126
consumer.onScanMetricsCreated(scanMetrics);
127+
if (this.scan.isScanMetricsByRegionEnabled()) {
128+
isScanMetricsByRegionEnabled = true;
129+
}
124130
} else {
125131
this.scanMetrics = null;
126132
}
133+
this.isScanMetricsByRegionEnabled = isScanMetricsByRegionEnabled;
127134

128135
/*
129136
* Assumes that the `start()` method is called immediately after construction. If this is no
@@ -250,6 +257,9 @@ private long getPrimaryTimeoutNs() {
250257
}
251258

252259
private void openScanner() {
260+
if (this.isScanMetricsByRegionEnabled) {
261+
scanMetrics.moveToNextRegion();
262+
}
253263
incRegionCountMetrics(scanMetrics);
254264
openScannerTries.set(1);
255265
addListener(timelineConsistentRead(conn.getLocator(), tableName, scan, scan.getStartRow(),
@@ -265,6 +275,11 @@ private void openScanner() {
265275
span.end();
266276
}
267277
}
278+
if (this.isScanMetricsByRegionEnabled) {
279+
HRegionLocation loc = resp.loc;
280+
this.scanMetrics.initScanMetricsRegionInfo(loc.getRegion().getEncodedName(),
281+
loc.getServerName());
282+
}
268283
startScan(resp);
269284
}
270285
});

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@
3434
import org.apache.hadoop.conf.Configuration;
3535
import org.apache.hadoop.hbase.DoNotRetryIOException;
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;
@@ -135,6 +137,11 @@ public ClientScanner(final Configuration conf, final Scan scan, final Scan scanF
135137
this.useScannerTimeoutForNextCalls = connectionConfiguration.isUseScannerTimeoutForNextCalls();
136138
this.requestAttributes = requestAttributes;
137139

140+
if (scan.isScanMetricsByRegionEnabled() && scan.getConsistency() == Consistency.TIMELINE) {
141+
scan.setEnableScanMetricsByRegion(false);
142+
scanForMetrics.setEnableScanMetricsByRegion(false);
143+
LOG.warn("Scan metrics by region is not supported for timeline consistency in HBase 2");
144+
}
138145
// check if application wants to collect scan metrics
139146
initScanMetrics(scan);
140147

@@ -259,6 +266,9 @@ protected boolean moveToNextRegion() {
259266
// clear the current region, we will set a new value to it after the first call of the new
260267
// callable.
261268
this.currentRegion = null;
269+
if (isScanMetricsByRegionEnabled()) {
270+
scanMetrics.moveToNextRegion();
271+
}
262272
this.callable = new ScannerCallableWithReplicas(getTable(), getConnection(),
263273
createScannerCallable(), pool, primaryOperationTimeout, scan, getRetries(), readRpcTimeout,
264274
scannerTimeout, useScannerTimeoutForNextCalls, caching, conf, caller);
@@ -281,6 +291,7 @@ private Result[] call(ScannerCallableWithReplicas callable, RpcRetryingCaller<Re
281291
Result[] rrs = caller.callWithoutRetries(callable, scannerTimeout);
282292
if (currentRegion == null && updateCurrentRegion) {
283293
currentRegion = callable.getHRegionInfo();
294+
initScanMetricsRegionInfo();
284295
}
285296
return rrs;
286297
}
@@ -469,7 +480,8 @@ protected void loadCache() throws IOException {
469480
}
470481
long currentTime = EnvironmentEdgeManager.currentTime();
471482
if (this.scanMetrics != null) {
472-
this.scanMetrics.sumOfMillisSecBetweenNexts.addAndGet(currentTime - lastNext);
483+
this.scanMetrics.addToCounter(ScanMetrics.MILLIS_BETWEEN_NEXTS_METRIC_NAME,
484+
currentTime - lastNext);
473485
}
474486
lastNext = currentTime;
475487
// Groom the array of Results that we received back from the server before adding that
@@ -622,4 +634,12 @@ public Result next() throws IOException {
622634
return nextWithSyncCache();
623635
}
624636
}
637+
638+
private void initScanMetricsRegionInfo() {
639+
if (isScanMetricsByRegionEnabled()) {
640+
HRegionLocation location = callable.getLocation();
641+
String encodedRegionName = location.getRegion().getEncodedName();
642+
scanMetrics.initScanMetricsRegionInfo(encodedRegionName, location.getServerName());
643+
}
644+
}
625645
}

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
@@ -367,19 +367,19 @@ static void incRPCCallsMetrics(ScanMetrics scanMetrics, boolean isRegionServerRe
367367
if (scanMetrics == null) {
368368
return;
369369
}
370-
scanMetrics.countOfRPCcalls.incrementAndGet();
370+
scanMetrics.addToCounter(ScanMetrics.RPC_CALLS_METRIC_NAME, 1);
371371
if (isRegionServerRemote) {
372-
scanMetrics.countOfRemoteRPCcalls.incrementAndGet();
372+
scanMetrics.addToCounter(ScanMetrics.REMOTE_RPC_CALLS_METRIC_NAME, 1);
373373
}
374374
}
375375

376376
static void incRPCRetriesMetrics(ScanMetrics scanMetrics, boolean isRegionServerRemote) {
377377
if (scanMetrics == null) {
378378
return;
379379
}
380-
scanMetrics.countOfRPCRetries.incrementAndGet();
380+
scanMetrics.addToCounter(ScanMetrics.RPC_RETRIES_METRIC_NAME, 1);
381381
if (isRegionServerRemote) {
382-
scanMetrics.countOfRemoteRPCRetries.incrementAndGet();
382+
scanMetrics.addToCounter(ScanMetrics.REMOTE_RPC_RETRIES_METRIC_NAME, 1);
383383
}
384384
}
385385

@@ -394,9 +394,9 @@ static void updateResultsMetrics(ScanMetrics scanMetrics, Result[] rrs,
394394
resultSize += PrivateCellUtil.estimatedSerializedSizeOf(cell);
395395
}
396396
}
397-
scanMetrics.countOfBytesInResults.addAndGet(resultSize);
397+
scanMetrics.addToCounter(ScanMetrics.BYTES_IN_RESULTS_METRIC_NAME, resultSize);
398398
if (isRegionServerRemote) {
399-
scanMetrics.countOfBytesInRemoteResults.addAndGet(resultSize);
399+
scanMetrics.addToCounter(ScanMetrics.BYTES_IN_REMOTE_RESULTS_METRIC_NAME, resultSize);
400400
}
401401
}
402402

@@ -416,7 +416,7 @@ static void incRegionCountMetrics(ScanMetrics scanMetrics) {
416416
if (scanMetrics == null) {
417417
return;
418418
}
419-
scanMetrics.countOfRegions.incrementAndGet();
419+
scanMetrics.addToCounter(ScanMetrics.REGIONS_SCANNED_METRIC_NAME, 1);
420420
}
421421

422422
/**

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}
@@ -1102,11 +1104,15 @@ public Scan setPriority(int priority) {
11021104
}
11031105

11041106
/**
1105-
* Enable collection of {@link ScanMetrics}. For advanced users.
1107+
* Enable collection of {@link ScanMetrics}. For advanced users. While disabling scan metrics,
1108+
* will also disable region level scan metrics.
11061109
* @param enabled Set to true to enable accumulating scan metrics
11071110
*/
11081111
public Scan setScanMetricsEnabled(final boolean enabled) {
11091112
setAttribute(Scan.SCAN_ATTRIBUTES_METRICS_ENABLE, Bytes.toBytes(Boolean.valueOf(enabled)));
1113+
if (!enabled) {
1114+
setEnableScanMetricsByRegion(false);
1115+
}
11101116
return this;
11111117
}
11121118

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

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
@@ -228,7 +228,7 @@ private ScanResponse next() throws IOException {
228228
// when what we need is to open scanner against new location.
229229
// Attach NSRE to signal client that it needs to re-setup scanner.
230230
if (this.scanMetrics != null) {
231-
this.scanMetrics.countOfNSRE.incrementAndGet();
231+
this.scanMetrics.addToCounter(ScanMetrics.NOT_SERVING_REGION_EXCEPTION_METRIC_NAME, 1);
232232
}
233233
throw new DoNotRetryIOException("Resetting the scanner -- see exception cause", ioe);
234234
} 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;
@@ -498,4 +499,8 @@ public String getExceptionMessageAdditionalDetail() {
498499
public long sleep(long pause, int tries) {
499500
return currentScannerCallable.sleep(pause, tries);
500501
}
502+
503+
public HRegionLocation getLocation() {
504+
return currentScannerCallable.getLocation();
505+
}
501506
}
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)