Skip to content

Conversation

@sanjeet006py
Copy link
Contributor

@sanjeet006py sanjeet006py commented Dec 26, 2025

JIRA: PHOENIX-7729

  • The change captures the metrics for top N slowest scans. The feature is controlled by two configs: phoenix.slowest.scan.metrics.count and phoenix.scan.metrics.by.region.enabled.
  • phoenix.slowest.scan.metrics.count specifies the value of N for capturing metrics for top N slowest scan metrics. The value should be > 0 to enable capturing top N slowest scan metrics.
  • phoenix.scan.metrics.by.region.enabled specifies whether to capture region hash and RS name along with HBase scan metrics. Leverages HBASE-29233.
  • Example of top N slowest scan metrics for N = 2:
[[{"table":"N000001","regions":[{"region":"e43c8b60c4cddf2acc0417d10ebbac24","server":"hbase.regionsserver.hostname1,52433,1763102072284","rsp":27,"rg":1,"rr":0,"brfc":0,"brfm":0,"ws":1,"rp":1,"broc":1,"rsqw":0,"brff":134,"rs":56,"frt":4,"n":28,"rrs":0,"wf":0,"rpr":0,"nsr":0,"rrr":0}]}],[{"table":"N000001","regions":[{"region":"9d77823d1e703b6c6d098006014f7a74","server":"hbase.regionsserver.hostname1,52433,1763102072284","rsp":27,"rg":1,"rr":0,"brfc":0,"brfm":0,"ws":1,"rp":1,"broc":1,"rsqw":0,"brff":128,"rs":50,"frt":4,"n":28,"rrs":0,"wf":0,"rpr":0,"nsr":0,"rrr":0}]}]]
  • For a query having subqueries or JOINs, we need to identify scans in query and subquery which together take most time. And, we need to find such top N list of scans for the given value of N. This PR also handles the aforementioned specified case. For example output, please refer to: SlowestScanMetricsIT.

<activation>
<property>
<name>!hbase.profile</name>
<name>hbase.profile</name>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert all the changes in this file before merge.

@sanjeet006py sanjeet006py marked this pull request as ready for review December 28, 2025 00:43
@sanjeet006py
Copy link
Contributor Author

Comment on lines 80 to 86
scan.setScanMetricsEnabled(true);
scan.setEnableScanMetricsByRegion(isScanMetricsByRegionEnabled);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 2 calls as a side effect of the getter method is not a good pattern.

globalScanMetrics.get(ScanMetrics.MILLIS_BETWEEN_NEXTS_METRIC_NAME);
}

private MetricType getMetricType(String metricName) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about moving this to MetricType enum? You could even add the HBase metric name to the enum constructor and create a lookup table dynamically by introspecting the enums with non-null HBase metric name. You can even have static getters to access the metric names and drive other parts of the code that have a long list of getters and setters to execute the logic reflectively instead of statically. A getter on the enum to access the hbase metric name can also help access the names via the enum for consistency.

@sanjeet006py sanjeet006py requested a review from haridsv December 31, 2025 23:08
public Iterator<ScanMetricsHolder> getIterator() {
return Collections.emptyIterator();
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This still creates an empty ConcurrentLinkedDeque that never gets used. You can add an alternative (private) constructor that takes a list and you can pass null for it.

CompatScanMetrics.getBytesReadFromBlockCache(scanMetricMap));
changeMetric(countOfBlockReadOps, CompatScanMetrics.getBlockReadOpsCount(scanMetricMap));
changeMetric(rpcScanProcessingTime, CompatScanMetrics.getRpcScanProcessingTime(scanMetricMap));
changeMetric(rpcScanQueueWaitTime, CompatScanMetrics.getRpcScanQueueWaitTime(scanMetricMap));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not have a `Map<MetricType, CombinableMetric> metrics field?

If you add something like List<MetricType> getHBaseBasedMetricTypes() to MetricType, then you can replace all but SCAN_BYTES and PAGED_ROWS_COUNTER with the below code:

    // Allocate all HBase-backed metrics in a loop
    for (MetricType metricType : MetricTypes.getHBaseBasedMetricTypes()) {
      metrics.put(metricType, readMetrics.allotMetric(metricType, tableName));
    }

As for the getters and setters, we can have one generic method like:

  public CombinableMetric getMetric(MetricType type) {
    return metrics.get(type);
  }

or if we prefer specific typed names, they can be changed like this:

  public CombinableMetric getCountOfRemoteRPCcalls() {
    return metrics.get(COUNT_REMOTE_RPC_CALLS);
  }

? props.getInt(QueryServices.SLOWEST_SCAN_METRICS_COUNT,
QueryServicesOptions.DEFAULT_SLOWEST_SCAN_METRICS_COUNT)
: Integer.parseInt(slowestScanMetricsCountStr);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Why not extract a generic method like getIntFromConnectionInfo() from the existing getMutateBatchSize and reuse it here?

}
return metrics;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this repeating method can be moved into JDBCUtil?

scanMetricsMap.get(ScanMetrics.COUNT_OF_ROWS_SCANNED_KEY_METRIC_NAME));
changeMetric(GLOBAL_HBASE_COUNT_ROWS_FILTERED,
scanMetricsMap.get(ScanMetrics.COUNT_OF_ROWS_FILTERED_KEY_METRIC_NAME));
changeMetric(GLOBAL_PAGED_ROWS_COUNTER, dummyRowCounter);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this code driven by a list?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants