-
Notifications
You must be signed in to change notification settings - Fork 3.4k
HBASE-29233: Capture scan metrics at region level #6868
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
271ff36
32a7e8e
a91e6fe
c88b6d7
5babeaa
bab2be1
8fc1be9
fdc8c5f
666e191
37e2c24
07c5628
0457771
4c840bb
e1f0470
4fa7f79
9f04f29
87c6cf9
0c65103
dfb41dd
011bae7
0cdc421
392dca3
9a9bd25
54189d8
bca08ef
2c7788c
4ded690
83081cb
746029e
4d2fdf8
286d7c9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,8 @@ | |
| */ | ||
| package org.apache.hadoop.hbase.client; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import org.apache.hadoop.hbase.client.metrics.ScanMetrics; | ||
| import org.apache.yetus.audience.InterfaceAudience; | ||
|
|
||
|
|
@@ -25,15 +27,33 @@ | |
| */ | ||
| @InterfaceAudience.Private | ||
| public abstract class AbstractClientScanner implements ResultScanner { | ||
| protected ScanMetrics scanMetrics; | ||
| protected ScanMetrics scanMetrics = null; | ||
| protected List<ScanMetrics> scanMetricsByRegion; | ||
|
|
||
| /** | ||
| * Check and initialize list for collecting scan metrics if application wants to collect scan | ||
| * metrics per region | ||
| */ | ||
| protected void initScanMetricsByRegion(Scan scan) { | ||
| // check if application wants to collect scan metrics | ||
| if (scan.isScanMetricsEnabled() && scan.isScanMetricsByRegionEnabled()) { | ||
| scanMetricsByRegion = new ArrayList<>(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Check and initialize if application wants to collect scan metrics | ||
| */ | ||
| protected void initScanMetrics(Scan scan) { | ||
| // check if application wants to collect scan metrics | ||
| if (scan.isScanMetricsEnabled()) { | ||
| scanMetrics = new ScanMetrics(); | ||
| if (scanMetricsByRegion != null) { | ||
| scanMetrics = new ScanMetrics(); | ||
| scanMetricsByRegion.add(scanMetrics); | ||
| } else if (scanMetrics == null) { | ||
| // Only initialize once | ||
| this.scanMetrics = new ScanMetrics(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -44,6 +64,24 @@ protected void initScanMetrics(Scan scan) { | |
| */ | ||
| @Override | ||
| public ScanMetrics getScanMetrics() { | ||
| return scanMetrics; | ||
| if (scanMetricsByRegion != null) { | ||
| if (scanMetricsByRegion.isEmpty()) { | ||
| return null; | ||
ndimiduk marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } else if (scanMetricsByRegion.size() == 1) { | ||
| return scanMetricsByRegion.get(0); | ||
| } | ||
| ScanMetrics overallScanMetrics = new ScanMetrics(); | ||
| for (ScanMetrics otherScanMetrics : scanMetricsByRegion) { | ||
| overallScanMetrics.combineMetrics(otherScanMetrics); | ||
| } | ||
| return overallScanMetrics; | ||
| } else { | ||
| return scanMetrics; | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public List<ScanMetrics> getScanMetricsByRegion() { | ||
|
||
| return scanMetricsByRegion; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -115,4 +115,8 @@ default Result[] next(int nbRows) throws IOException { | |
|
|
||
| /** Returns the scan metrics, or {@code null} if we do not enable metrics. */ | ||
| ScanMetrics getScanMetrics(); | ||
|
|
||
| default List<ScanMetrics> getScanMetricsByRegion() { | ||
| throw new UnsupportedOperationException("Scan Metrics by region is not supported"); | ||
|
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ | |
| */ | ||
| package org.apache.hadoop.hbase.client; | ||
|
|
||
| import java.util.List; | ||
| import org.apache.hadoop.hbase.client.metrics.ScanMetrics; | ||
| import org.apache.yetus.audience.InterfaceAudience; | ||
|
|
||
|
|
@@ -38,11 +39,22 @@ public interface ScanResultConsumerBase { | |
| 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. | ||
| * If {@code scan.isScanMetricsEnabled()} returns true and | ||
| * {@code scan.isScanMetricsByRegionEnabled()} returns false, 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) { | ||
| } | ||
|
|
||
| /** | ||
| * If {@code scan.isScanMetricsEnabled()} and {@code scan.isScanMetricsByRegionEnabled()} both | ||
| * return true, then this method will be called prior to calling all the methods in this interface | ||
| * to give you the list for storing scan metric per region for this scan operation. The list will | ||
| * be get populated with per region scan metrics on-the-fly during the scan, you can store the | ||
| * provided list somewhere to get scan metrics by region later when you want. | ||
| */ | ||
| default void onScanMetricsByRegionEnabled(List<ScanMetrics> scanMetricsByRegion) { | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,7 +19,9 @@ | |
|
|
||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
| import java.util.Objects; | ||
| import java.util.concurrent.atomic.AtomicLong; | ||
| import org.apache.hadoop.hbase.ServerName; | ||
| import org.apache.yetus.audience.InterfaceAudience; | ||
|
|
||
| import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableMap; | ||
|
|
@@ -33,7 +35,9 @@ public class ServerSideScanMetrics { | |
| /** | ||
| * Hash to hold the String -> Atomic Long mappings for each metric | ||
| */ | ||
| private final Map<String, AtomicLong> counters = new HashMap<>(); | ||
| protected final Map<String, AtomicLong> counters = new HashMap<>(); | ||
| private ServerName serverName; | ||
| private String encodedRegionName; | ||
|
|
||
| /** | ||
| * Create a new counter with the specified name | ||
|
|
@@ -117,4 +121,64 @@ public Map<String, Long> getMetricsMap(boolean reset) { | |
| // Build the immutable map so that people can't mess around with it. | ||
| return builder.build(); | ||
| } | ||
|
|
||
| public ServerName getServerName() { | ||
| return this.serverName; | ||
| } | ||
|
|
||
| public void setServerName(ServerName serverName) { | ||
| if (this.serverName == null) { | ||
| this.serverName = serverName; | ||
| } | ||
| } | ||
|
|
||
| public void setEncodedRegionName(String encodedRegionName) { | ||
| if (this.encodedRegionName == null) { | ||
| this.encodedRegionName = encodedRegionName; | ||
| } | ||
| } | ||
|
|
||
| public String getEncodedRegionName() { | ||
| return this.encodedRegionName; | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| StringBuilder sb = new StringBuilder(); | ||
| sb.append("ServerName:"); | ||
| sb.append(this.serverName); | ||
| sb.append(",EncodedRegion:"); | ||
| sb.append(this.encodedRegionName); | ||
| sb.append(",["); | ||
| boolean isFirstMetric = true; | ||
| for (Map.Entry<String, AtomicLong> e : this.counters.entrySet()) { | ||
| if (isFirstMetric) { | ||
| isFirstMetric = false; | ||
| } else { | ||
| sb.append(","); | ||
| } | ||
| sb.append(e.getKey()); | ||
| sb.append(":"); | ||
| sb.append(e.getValue().get()); | ||
| } | ||
| sb.append("]"); | ||
| return sb.toString(); | ||
| } | ||
ndimiduk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| public void combineMetrics(ScanMetrics other) { | ||
| for (Map.Entry<String, AtomicLong> entry : other.counters.entrySet()) { | ||
| String counterName = entry.getKey(); | ||
| AtomicLong counter = entry.getValue(); | ||
| this.addToCounter(counterName, counter.get()); | ||
| } | ||
| if ( | ||
| this.encodedRegionName != null | ||
| && !Objects.equals(this.encodedRegionName, other.getEncodedRegionName()) | ||
| ) { | ||
| this.encodedRegionName = null; | ||
| } | ||
| if (this.serverName != null && !Objects.equals(this.serverName, other.getServerName())) { | ||
| this.serverName = null; | ||
|
||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Over-all question: why do you always add this
List<ScanMetrics> scanMetricsByRegioninstead of using the existing instanceScanMetrics scanMetricsas an accumulator and fold in the new metrics as they arrive into this instance? You could initialize it once and have less null-checking going on. You do this in all the classes that interact with this new feature.