Skip to content

Commit 73a3a26

Browse files
sanjeet006pySanjeet Malhotra
authored andcommitted
HBASE-29233: Capture scan metrics at region level (apache#6868)
Signed-off-by: Viraj Jasani <[email protected]>
1 parent 71d68e8 commit 73a3a26

22 files changed

+1700
-55
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/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
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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("ScanMetricsRegionInfo has already been initialized");
71+
}
72+
}
73+
74+
ScanMetricsRegionInfo getScanMetricsRegionInfo() {
75+
return scanMetricsRegionInfo;
76+
}
77+
}

hbase-client/src/main/java/org/apache/hadoop/hbase/client/metrics/ScanMetrics.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,22 @@ public class ScanMetrics extends ServerSideScanMetrics {
9696
public final AtomicLong countOfRemoteRPCRetries = createCounter(REMOTE_RPC_RETRIES_METRIC_NAME);
9797

9898
/**
99-
* constructor
99+
* Constructor
100100
*/
101101
public ScanMetrics() {
102102
}
103+
104+
@Override
105+
public void moveToNextRegion() {
106+
super.moveToNextRegion();
107+
currentRegionScanMetricsData.createCounter(RPC_CALLS_METRIC_NAME);
108+
currentRegionScanMetricsData.createCounter(REMOTE_RPC_CALLS_METRIC_NAME);
109+
currentRegionScanMetricsData.createCounter(MILLIS_BETWEEN_NEXTS_METRIC_NAME);
110+
currentRegionScanMetricsData.createCounter(NOT_SERVING_REGION_EXCEPTION_METRIC_NAME);
111+
currentRegionScanMetricsData.createCounter(BYTES_IN_RESULTS_METRIC_NAME);
112+
currentRegionScanMetricsData.createCounter(BYTES_IN_REMOTE_RESULTS_METRIC_NAME);
113+
currentRegionScanMetricsData.createCounter(REGIONS_SCANNED_METRIC_NAME);
114+
currentRegionScanMetricsData.createCounter(RPC_RETRIES_METRIC_NAME);
115+
currentRegionScanMetricsData.createCounter(REMOTE_RPC_RETRIES_METRIC_NAME);
116+
}
103117
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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 org.apache.commons.lang3.builder.EqualsBuilder;
21+
import org.apache.commons.lang3.builder.HashCodeBuilder;
22+
import org.apache.hadoop.hbase.ServerName;
23+
import org.apache.yetus.audience.InterfaceAudience;
24+
import org.apache.yetus.audience.InterfaceStability;
25+
26+
/**
27+
* POJO for capturing region level details when region level scan metrics are enabled. <br>
28+
* <br>
29+
* Currently, encoded region name and server name (host name, ports and startcode) are captured as
30+
* region details. <br>
31+
* <br>
32+
* Instance of this class serves as key in the Map returned by
33+
* {@link ServerSideScanMetrics#collectMetricsByRegion()} or
34+
* {@link ServerSideScanMetrics#collectMetricsByRegion(boolean)}.
35+
*/
36+
@InterfaceAudience.Public
37+
@InterfaceStability.Evolving
38+
public class ScanMetricsRegionInfo {
39+
/**
40+
* Users should only compare against this constant by reference and should not make any
41+
* assumptions regarding content of the constant.
42+
*/
43+
public static final ScanMetricsRegionInfo EMPTY_SCAN_METRICS_REGION_INFO =
44+
new ScanMetricsRegionInfo(null, null);
45+
46+
private final String encodedRegionName;
47+
private final ServerName serverName;
48+
49+
ScanMetricsRegionInfo(String encodedRegionName, ServerName serverName) {
50+
this.encodedRegionName = encodedRegionName;
51+
this.serverName = serverName;
52+
}
53+
54+
@Override
55+
public boolean equals(Object obj) {
56+
if (!(obj instanceof ScanMetricsRegionInfo other)) {
57+
return false;
58+
}
59+
return new EqualsBuilder().append(encodedRegionName, other.encodedRegionName)
60+
.append(serverName, other.serverName).isEquals();
61+
}
62+
63+
@Override
64+
public int hashCode() {
65+
return new HashCodeBuilder(17, 37).append(encodedRegionName).append(serverName).toHashCode();
66+
}
67+
68+
@Override
69+
public String toString() {
70+
return getClass().getSimpleName() + "[encodedRegionName=" + encodedRegionName + ",serverName="
71+
+ serverName + "]";
72+
}
73+
74+
public String getEncodedRegionName() {
75+
return encodedRegionName;
76+
}
77+
78+
public ServerName getServerName() {
79+
return serverName;
80+
}
81+
}

0 commit comments

Comments
 (0)