001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.client.metrics; 019 020import java.util.ArrayList; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.concurrent.atomic.AtomicLong; 025import java.util.stream.Collectors; 026import org.apache.hadoop.hbase.ServerName; 027import org.apache.yetus.audience.InterfaceAudience; 028 029import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableMap; 030 031/** 032 * Provides server side metrics related to scan operations. 033 */ 034@InterfaceAudience.Public 035@SuppressWarnings("checkstyle:VisibilityModifier") // See HBASE-27757 036public class ServerSideScanMetrics { 037 /** 038 * Hash to hold the String -> Atomic Long mappings for each metric 039 */ 040 private final Map<String, AtomicLong> counters = new HashMap<>(); 041 private final List<RegionScanMetricsData> regionScanMetricsData = new ArrayList<>(0); 042 protected RegionScanMetricsData currentRegionScanMetricsData = null; 043 044 /** 045 * If region level scan metrics are enabled, must call this method to start collecting metrics for 046 * the region before scanning the region. 047 */ 048 public void moveToNextRegion() { 049 currentRegionScanMetricsData = new RegionScanMetricsData(); 050 regionScanMetricsData.add(currentRegionScanMetricsData); 051 currentRegionScanMetricsData.createCounter(COUNT_OF_ROWS_SCANNED_KEY_METRIC_NAME); 052 currentRegionScanMetricsData.createCounter(COUNT_OF_ROWS_FILTERED_KEY_METRIC_NAME); 053 currentRegionScanMetricsData.createCounter(BLOCK_BYTES_SCANNED_KEY_METRIC_NAME); 054 currentRegionScanMetricsData.createCounter(FS_READ_TIME_METRIC_NAME); 055 currentRegionScanMetricsData.createCounter(BYTES_READ_FROM_FS_METRIC_NAME); 056 currentRegionScanMetricsData.createCounter(BYTES_READ_FROM_BLOCK_CACHE_METRIC_NAME); 057 currentRegionScanMetricsData.createCounter(BYTES_READ_FROM_MEMSTORE_METRIC_NAME); 058 currentRegionScanMetricsData.createCounter(BLOCK_READ_OPS_COUNT_METRIC_NAME); 059 } 060 061 /** 062 * Create a new counter with the specified name. 063 * @return {@link AtomicLong} instance for the counter with counterName 064 */ 065 protected AtomicLong createCounter(String counterName) { 066 return ScanMetricsUtil.createCounter(counters, counterName); 067 } 068 069 public static final String COUNT_OF_ROWS_SCANNED_KEY_METRIC_NAME = "ROWS_SCANNED"; 070 public static final String COUNT_OF_ROWS_FILTERED_KEY_METRIC_NAME = "ROWS_FILTERED"; 071 072 public static final String BLOCK_BYTES_SCANNED_KEY_METRIC_NAME = "BLOCK_BYTES_SCANNED"; 073 074 public static final String FS_READ_TIME_METRIC_NAME = "FS_READ_TIME"; 075 public static final String BYTES_READ_FROM_FS_METRIC_NAME = "BYTES_READ_FROM_FS"; 076 public static final String BYTES_READ_FROM_BLOCK_CACHE_METRIC_NAME = 077 "BYTES_READ_FROM_BLOCK_CACHE"; 078 public static final String BYTES_READ_FROM_MEMSTORE_METRIC_NAME = "BYTES_READ_FROM_MEMSTORE"; 079 public static final String BLOCK_READ_OPS_COUNT_METRIC_NAME = "BLOCK_READ_OPS_COUNT"; 080 081 /** 082 * number of rows filtered during scan RPC 083 */ 084 public final AtomicLong countOfRowsFiltered = 085 createCounter(COUNT_OF_ROWS_FILTERED_KEY_METRIC_NAME); 086 087 /** 088 * number of rows scanned during scan RPC. Not every row scanned will be returned to the client 089 * since rows may be filtered. 090 */ 091 public final AtomicLong countOfRowsScanned = createCounter(COUNT_OF_ROWS_SCANNED_KEY_METRIC_NAME); 092 093 public final AtomicLong countOfBlockBytesScanned = 094 createCounter(BLOCK_BYTES_SCANNED_KEY_METRIC_NAME); 095 096 public final AtomicLong fsReadTime = createCounter(FS_READ_TIME_METRIC_NAME); 097 098 public final AtomicLong bytesReadFromFs = createCounter(BYTES_READ_FROM_FS_METRIC_NAME); 099 100 public final AtomicLong bytesReadFromBlockCache = 101 createCounter(BYTES_READ_FROM_BLOCK_CACHE_METRIC_NAME); 102 103 public final AtomicLong bytesReadFromMemstore = 104 createCounter(BYTES_READ_FROM_MEMSTORE_METRIC_NAME); 105 106 public final AtomicLong blockReadOpsCount = createCounter(BLOCK_READ_OPS_COUNT_METRIC_NAME); 107 108 /** 109 * Sets counter with counterName to passed in value, does nothing if counter does not exist. If 110 * region level scan metrics are enabled then sets the value of counter for the current region 111 * being scanned. 112 */ 113 public void setCounter(String counterName, long value) { 114 ScanMetricsUtil.setCounter(counters, counterName, value); 115 if (this.currentRegionScanMetricsData != null) { 116 this.currentRegionScanMetricsData.setCounter(counterName, value); 117 } 118 } 119 120 /** 121 * Returns true if a counter exists with the counterName. 122 */ 123 public boolean hasCounter(String counterName) { 124 return ScanMetricsUtil.hasCounter(counters, counterName); 125 } 126 127 /** 128 * Returns {@link AtomicLong} instance for this counter name, null if counter does not exist. 129 */ 130 public AtomicLong getCounter(String counterName) { 131 return ScanMetricsUtil.getCounter(counters, counterName); 132 } 133 134 /** 135 * Increments the counter with counterName by delta, does nothing if counter does not exist. If 136 * region level scan metrics are enabled then increments the counter corresponding to the current 137 * region being scanned. Please see {@link #moveToNextRegion()}. 138 */ 139 public void addToCounter(String counterName, long delta) { 140 ScanMetricsUtil.addToCounter(counters, counterName, delta); 141 if (this.currentRegionScanMetricsData != null) { 142 this.currentRegionScanMetricsData.addToCounter(counterName, delta); 143 } 144 } 145 146 /** 147 * Get all the values combined for all the regions since the last time this function was called. 148 * Calling this function will reset all AtomicLongs in the instance back to 0. 149 * @return A Map of String -> Long for metrics 150 */ 151 public Map<String, Long> getMetricsMap() { 152 return getMetricsMap(true); 153 } 154 155 /** 156 * Get all the values combined for all the regions. If reset is true, we will reset all the 157 * AtomicLongs back to 0. 158 * @param reset whether to reset the AtomicLongs to 0. 159 * @return A Map of String -> Long for metrics 160 */ 161 public Map<String, Long> getMetricsMap(boolean reset) { 162 return ImmutableMap.copyOf(ScanMetricsUtil.collectMetrics(counters, reset)); 163 } 164 165 /** 166 * Get values grouped by each region scanned since the last time this was called. Calling this 167 * function will reset all region level scan metrics counters back to 0. 168 * @return A Map of region -> (Map of metric name -> Long) for metrics 169 */ 170 public Map<ScanMetricsRegionInfo, Map<String, Long>> collectMetricsByRegion() { 171 return collectMetricsByRegion(true); 172 } 173 174 /** 175 * Get values grouped by each region scanned. If reset is true, will reset all the region level 176 * scan metrics counters back to 0. 177 * @param reset whether to reset region level scan metric counters to 0. 178 * @return A Map of region -> (Map of metric name -> Long) for metrics 179 */ 180 public Map<ScanMetricsRegionInfo, Map<String, Long>> collectMetricsByRegion(boolean reset) { 181 // Create a builder 182 ImmutableMap.Builder<ScanMetricsRegionInfo, Map<String, Long>> builder = ImmutableMap.builder(); 183 for (RegionScanMetricsData regionScanMetricsData : this.regionScanMetricsData) { 184 if ( 185 regionScanMetricsData.getScanMetricsRegionInfo() 186 == ScanMetricsRegionInfo.EMPTY_SCAN_METRICS_REGION_INFO 187 ) { 188 continue; 189 } 190 builder.put(regionScanMetricsData.getScanMetricsRegionInfo(), 191 regionScanMetricsData.collectMetrics(reset)); 192 } 193 return builder.build(); 194 } 195 196 @Override 197 public String toString() { 198 return counters + "," + regionScanMetricsData.stream().map(RegionScanMetricsData::toString) 199 .collect(Collectors.joining(",")); 200 } 201 202 /** 203 * Call this method after calling {@link #moveToNextRegion()} to populate server name and encoded 204 * region name details for the region being scanned and for which metrics are being collected at 205 * the moment. 206 */ 207 public void initScanMetricsRegionInfo(String encodedRegionName, ServerName serverName) { 208 currentRegionScanMetricsData.initScanMetricsRegionInfo(encodedRegionName, serverName); 209 } 210}