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 currentRegionScanMetricsData.createCounter(RPC_SCAN_PROCESSING_TIME_METRIC_NAME); 060 currentRegionScanMetricsData.createCounter(RPC_SCAN_QUEUE_WAIT_TIME_METRIC_NAME); 061 } 062 063 /** 064 * Create a new counter with the specified name. 065 * @return {@link AtomicLong} instance for the counter with counterName 066 */ 067 protected AtomicLong createCounter(String counterName) { 068 return ScanMetricsUtil.createCounter(counters, counterName); 069 } 070 071 public static final String COUNT_OF_ROWS_SCANNED_KEY_METRIC_NAME = "ROWS_SCANNED"; 072 public static final String COUNT_OF_ROWS_FILTERED_KEY_METRIC_NAME = "ROWS_FILTERED"; 073 074 public static final String BLOCK_BYTES_SCANNED_KEY_METRIC_NAME = "BLOCK_BYTES_SCANNED"; 075 076 public static final String FS_READ_TIME_METRIC_NAME = "FS_READ_TIME"; 077 public static final String BYTES_READ_FROM_FS_METRIC_NAME = "BYTES_READ_FROM_FS"; 078 public static final String BYTES_READ_FROM_BLOCK_CACHE_METRIC_NAME = 079 "BYTES_READ_FROM_BLOCK_CACHE"; 080 public static final String BYTES_READ_FROM_MEMSTORE_METRIC_NAME = "BYTES_READ_FROM_MEMSTORE"; 081 public static final String BLOCK_READ_OPS_COUNT_METRIC_NAME = "BLOCK_READ_OPS_COUNT"; 082 public static final String RPC_SCAN_PROCESSING_TIME_METRIC_NAME = "RPC_SCAN_PROCESSING_TIME"; 083 public static final String RPC_SCAN_QUEUE_WAIT_TIME_METRIC_NAME = "RPC_SCAN_QUEUE_WAIT_TIME"; 084 085 /** 086 * number of rows filtered during scan RPC 087 */ 088 public final AtomicLong countOfRowsFiltered = 089 createCounter(COUNT_OF_ROWS_FILTERED_KEY_METRIC_NAME); 090 091 /** 092 * number of rows scanned during scan RPC. Not every row scanned will be returned to the client 093 * since rows may be filtered. 094 */ 095 public final AtomicLong countOfRowsScanned = createCounter(COUNT_OF_ROWS_SCANNED_KEY_METRIC_NAME); 096 097 public final AtomicLong countOfBlockBytesScanned = 098 createCounter(BLOCK_BYTES_SCANNED_KEY_METRIC_NAME); 099 100 public final AtomicLong fsReadTime = createCounter(FS_READ_TIME_METRIC_NAME); 101 102 public final AtomicLong bytesReadFromFs = createCounter(BYTES_READ_FROM_FS_METRIC_NAME); 103 104 public final AtomicLong bytesReadFromBlockCache = 105 createCounter(BYTES_READ_FROM_BLOCK_CACHE_METRIC_NAME); 106 107 public final AtomicLong bytesReadFromMemstore = 108 createCounter(BYTES_READ_FROM_MEMSTORE_METRIC_NAME); 109 110 public final AtomicLong blockReadOpsCount = createCounter(BLOCK_READ_OPS_COUNT_METRIC_NAME); 111 112 public final AtomicLong rpcScanProcessingTime = 113 createCounter(RPC_SCAN_PROCESSING_TIME_METRIC_NAME); 114 115 public final AtomicLong rpcScanQueueWaitTime = 116 createCounter(RPC_SCAN_QUEUE_WAIT_TIME_METRIC_NAME); 117 118 /** 119 * Sets counter with counterName to passed in value, does nothing if counter does not exist. If 120 * region level scan metrics are enabled then sets the value of counter for the current region 121 * being scanned. 122 */ 123 public void setCounter(String counterName, long value) { 124 ScanMetricsUtil.setCounter(counters, counterName, value); 125 if (this.currentRegionScanMetricsData != null) { 126 this.currentRegionScanMetricsData.setCounter(counterName, value); 127 } 128 } 129 130 /** 131 * Returns true if a counter exists with the counterName. 132 */ 133 public boolean hasCounter(String counterName) { 134 return ScanMetricsUtil.hasCounter(counters, counterName); 135 } 136 137 /** 138 * Returns {@link AtomicLong} instance for this counter name, null if counter does not exist. 139 */ 140 public AtomicLong getCounter(String counterName) { 141 return ScanMetricsUtil.getCounter(counters, counterName); 142 } 143 144 /** 145 * Increments the counter with counterName by delta, does nothing if counter does not exist. If 146 * region level scan metrics are enabled then increments the counter corresponding to the current 147 * region being scanned. Please see {@link #moveToNextRegion()}. 148 */ 149 public void addToCounter(String counterName, long delta) { 150 ScanMetricsUtil.addToCounter(counters, counterName, delta); 151 if (this.currentRegionScanMetricsData != null) { 152 this.currentRegionScanMetricsData.addToCounter(counterName, delta); 153 } 154 } 155 156 /** 157 * Get all the values combined for all the regions since the last time this function was called. 158 * Calling this function will reset all AtomicLongs in the instance back to 0. 159 * @return A Map of String -> Long for metrics 160 */ 161 public Map<String, Long> getMetricsMap() { 162 return getMetricsMap(true); 163 } 164 165 /** 166 * Get all the values combined for all the regions. If reset is true, we will reset all the 167 * AtomicLongs back to 0. 168 * @param reset whether to reset the AtomicLongs to 0. 169 * @return A Map of String -> Long for metrics 170 */ 171 public Map<String, Long> getMetricsMap(boolean reset) { 172 return ImmutableMap.copyOf(ScanMetricsUtil.collectMetrics(counters, reset)); 173 } 174 175 /** 176 * Get values grouped by each region scanned since the last time this was called. Calling this 177 * function will reset all region level scan metrics counters back to 0. 178 * @return A Map of region -> (Map of metric name -> Long) for metrics 179 */ 180 public Map<ScanMetricsRegionInfo, Map<String, Long>> collectMetricsByRegion() { 181 return collectMetricsByRegion(true); 182 } 183 184 /** 185 * Get values grouped by each region scanned. If reset is true, will reset all the region level 186 * scan metrics counters back to 0. 187 * @param reset whether to reset region level scan metric counters to 0. 188 * @return A Map of region -> (Map of metric name -> Long) for metrics 189 */ 190 public Map<ScanMetricsRegionInfo, Map<String, Long>> collectMetricsByRegion(boolean reset) { 191 // Create a builder 192 ImmutableMap.Builder<ScanMetricsRegionInfo, Map<String, Long>> builder = ImmutableMap.builder(); 193 for (RegionScanMetricsData regionScanMetricsData : this.regionScanMetricsData) { 194 if ( 195 regionScanMetricsData.getScanMetricsRegionInfo() 196 == ScanMetricsRegionInfo.EMPTY_SCAN_METRICS_REGION_INFO 197 ) { 198 continue; 199 } 200 builder.put(regionScanMetricsData.getScanMetricsRegionInfo(), 201 regionScanMetricsData.collectMetrics(reset)); 202 } 203 return builder.build(); 204 } 205 206 @Override 207 public String toString() { 208 return counters + "," + regionScanMetricsData.stream().map(RegionScanMetricsData::toString) 209 .collect(Collectors.joining(",")); 210 } 211 212 /** 213 * Call this method after calling {@link #moveToNextRegion()} to populate server name and encoded 214 * region name details for the region being scanned and for which metrics are being collected at 215 * the moment. 216 */ 217 public void initScanMetricsRegionInfo(String encodedRegionName, ServerName serverName) { 218 currentRegionScanMetricsData.initScanMetricsRegionInfo(encodedRegionName, serverName); 219 } 220}