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.regionserver; 019 020import java.io.Closeable; 021import java.io.IOException; 022import java.util.HashMap; 023import java.util.Map; 024import java.util.OptionalDouble; 025import java.util.OptionalLong; 026import java.util.concurrent.ScheduledExecutorService; 027import java.util.concurrent.ScheduledFuture; 028import java.util.concurrent.TimeUnit; 029import org.apache.commons.lang3.mutable.MutableLong; 030import org.apache.hadoop.hbase.CompatibilitySingletonFactory; 031import org.apache.hadoop.hbase.client.RegionInfo; 032import org.apache.hadoop.hbase.client.TableDescriptor; 033import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 034import org.apache.hadoop.hbase.util.Pair; 035import org.apache.hadoop.metrics2.MetricsExecutor; 036import org.apache.yetus.audience.InterfaceAudience; 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039 040@InterfaceAudience.Private 041public class MetricsRegionWrapperImpl implements MetricsRegionWrapper, Closeable { 042 043 private static final Logger LOG = LoggerFactory.getLogger(MetricsRegionWrapperImpl.class); 044 045 public static final int PERIOD = 45; 046 public static final String UNKNOWN = "unknown"; 047 048 private final HRegion region; 049 private ScheduledExecutorService executor; 050 private Runnable runnable; 051 private long numStoreFiles; 052 private long storeRefCount; 053 private long maxCompactedStoreFileRefCount; 054 private long memstoreSize; 055 private long memstoreHeapSize; 056 private long memstoreOffHeapSize; 057 private long storeFileSize; 058 private long maxStoreFileAge; 059 private long minStoreFileAge; 060 private long avgStoreFileAge; 061 private long numReferenceFiles; 062 private long maxFlushQueueSize; 063 private long maxCompactionQueueSize; 064 private Map<String, Long> readsOnlyFromMemstore; 065 private Map<String, Long> mixedReadsOnStore; 066 067 private ScheduledFuture<?> regionMetricsUpdateTask; 068 069 private float currentRegionCacheRatio; 070 private final String tableDescriptorHash; 071 072 private float currentRegionColdDataRatio; 073 074 public MetricsRegionWrapperImpl(HRegion region) { 075 this.region = region; 076 this.tableDescriptorHash = determineTableDescriptorHash(); 077 this.executor = CompatibilitySingletonFactory.getInstance(MetricsExecutor.class).getExecutor(); 078 this.runnable = new HRegionMetricsWrapperRunnable(); 079 this.regionMetricsUpdateTask = 080 this.executor.scheduleWithFixedDelay(this.runnable, PERIOD, PERIOD, TimeUnit.SECONDS); 081 } 082 083 @Override 084 public String getTableName() { 085 TableDescriptor tableDesc = this.region.getTableDescriptor(); 086 if (tableDesc == null) { 087 return UNKNOWN; 088 } 089 return tableDesc.getTableName().getQualifierAsString(); 090 } 091 092 @Override 093 public String getNamespace() { 094 TableDescriptor tableDesc = this.region.getTableDescriptor(); 095 if (tableDesc == null) { 096 return UNKNOWN; 097 } 098 return tableDesc.getTableName().getNamespaceAsString(); 099 } 100 101 @Override 102 public String getRegionName() { 103 RegionInfo regionInfo = this.region.getRegionInfo(); 104 if (regionInfo == null) { 105 return UNKNOWN; 106 } 107 return regionInfo.getEncodedName(); 108 } 109 110 @Override 111 public long getNumStores() { 112 Map<byte[], HStore> stores = this.region.stores; 113 if (stores == null) { 114 return 0; 115 } 116 return stores.size(); 117 } 118 119 @Override 120 public long getNumStoreFiles() { 121 return numStoreFiles; 122 } 123 124 @Override 125 public long getMemStoreSize() { 126 return memstoreSize; 127 } 128 129 @Override 130 public long getMemStoreHeapSize() { 131 return memstoreHeapSize; 132 } 133 134 @Override 135 public long getMemStoreOffHeapSize() { 136 return memstoreOffHeapSize; 137 } 138 139 @Override 140 public long getStoreFileSize() { 141 return storeFileSize; 142 } 143 144 public float getCurrentRegionCacheRatio() { 145 return currentRegionCacheRatio; 146 } 147 148 public float getCurrentRegionColdDataRatio() { 149 return currentRegionColdDataRatio; 150 } 151 152 @Override 153 public long getStoreRefCount() { 154 return storeRefCount; 155 } 156 157 @Override 158 public long getMaxCompactedStoreFileRefCount() { 159 return maxCompactedStoreFileRefCount; 160 } 161 162 @Override 163 public long getReadRequestCount() { 164 return this.region.getReadRequestsCount(); 165 } 166 167 @Override 168 public long getCpRequestCount() { 169 return this.region.getCpRequestsCount(); 170 } 171 172 @Override 173 public long getFilteredReadRequestCount() { 174 return this.region.getFilteredReadRequestsCount(); 175 } 176 177 @Override 178 public long getWriteRequestCount() { 179 return this.region.getWriteRequestsCount(); 180 } 181 182 @Override 183 public long getNumFilesCompacted() { 184 return this.region.compactionNumFilesCompacted.sum(); 185 } 186 187 @Override 188 public long getNumBytesCompacted() { 189 return this.region.compactionNumBytesCompacted.sum(); 190 } 191 192 @Override 193 public long getNumCompactionsCompleted() { 194 return this.region.compactionsFinished.sum(); 195 } 196 197 @Override 198 public long getLastMajorCompactionAge() { 199 long lastMajorCompactionTs = 0L; 200 try { 201 lastMajorCompactionTs = this.region.getOldestHfileTs(true); 202 } catch (IOException ioe) { 203 LOG.error("Could not load HFile info ", ioe); 204 } 205 long now = EnvironmentEdgeManager.currentTime(); 206 return now - lastMajorCompactionTs; 207 } 208 209 @Override 210 public long getTotalRequestCount() { 211 return getReadRequestCount() + getWriteRequestCount(); 212 } 213 214 @Override 215 public long getNumCompactionsFailed() { 216 return this.region.compactionsFailed.sum(); 217 } 218 219 @Override 220 public long getNumCompactionsQueued() { 221 return this.region.compactionsQueued.sum(); 222 } 223 224 @Override 225 public long getNumFlushesQueued() { 226 return this.region.flushesQueued.sum(); 227 } 228 229 @Override 230 public long getMaxCompactionQueueSize() { 231 return maxCompactionQueueSize; 232 } 233 234 @Override 235 public long getMaxFlushQueueSize() { 236 return maxFlushQueueSize; 237 } 238 239 @Override 240 public long getMaxStoreFileAge() { 241 return maxStoreFileAge; 242 } 243 244 @Override 245 public long getMinStoreFileAge() { 246 return minStoreFileAge; 247 } 248 249 @Override 250 public long getAvgStoreFileAge() { 251 return avgStoreFileAge; 252 } 253 254 @Override 255 public long getNumReferenceFiles() { 256 return numReferenceFiles; 257 } 258 259 @Override 260 public int getRegionHashCode() { 261 return this.region.hashCode(); 262 } 263 264 @Override 265 public Map<String, Long> getMemstoreOnlyRowReadsCount() { 266 return readsOnlyFromMemstore; 267 } 268 269 @Override 270 public Map<String, Long> getMixedRowReadsCount() { 271 return mixedReadsOnStore; 272 } 273 274 public class HRegionMetricsWrapperRunnable implements Runnable { 275 276 @Override 277 public void run() { 278 // The executor only reschedules this task (scheduleWithFixedDelay) if run() returns 279 // normally. Any Throwable that escapes here (e.g. an NPE from a region closing mid-scan) 280 // is swallowed by the underlying ScheduledFutureTask with no log output anywhere, and this 281 // task permanently stops running. Catch everything so a failure is visible AND the next 282 // scheduled run still happens. 283 try { 284 long tempNumStoreFiles = 0; 285 int tempStoreRefCount = 0; 286 int tempMaxCompactedStoreFileRefCount = 0; 287 long tempMemstoreSize = 0; 288 long tempMemstoreHeapSize = 0; 289 long tempMemstoreOffHeapSize = 0; 290 long tempStoreFileSize = 0; 291 long tempMaxStoreFileAge = 0; 292 long tempMinStoreFileAge = Long.MAX_VALUE; 293 long tempNumReferenceFiles = 0; 294 long tempMaxCompactionQueueSize = 0; 295 long tempMaxFlushQueueSize = 0; 296 long avgAgeNumerator = 0; 297 long numHFiles = 0; 298 if (region.stores != null) { 299 for (HStore store : region.stores.values()) { 300 tempNumStoreFiles += store.getStorefilesCount(); 301 int currentStoreRefCount = store.getStoreRefCount(); 302 tempStoreRefCount += currentStoreRefCount; 303 int currentMaxCompactedStoreFileRefCount = store.getMaxCompactedStoreFileRefCount(); 304 tempMaxCompactedStoreFileRefCount = 305 Math.max(tempMaxCompactedStoreFileRefCount, currentMaxCompactedStoreFileRefCount); 306 final MemStoreSize memStore = store.getMemStoreSize(); 307 tempMemstoreSize += memStore.getDataSize(); 308 tempMemstoreHeapSize += memStore.getHeapSize(); 309 tempMemstoreOffHeapSize += memStore.getOffHeapSize(); 310 tempStoreFileSize += store.getStorefilesSize(); 311 OptionalLong storeMaxStoreFileAge = store.getMaxStoreFileAge(); 312 if ( 313 storeMaxStoreFileAge.isPresent() 314 && storeMaxStoreFileAge.getAsLong() > tempMaxStoreFileAge 315 ) { 316 tempMaxStoreFileAge = storeMaxStoreFileAge.getAsLong(); 317 } 318 319 OptionalLong storeMinStoreFileAge = store.getMinStoreFileAge(); 320 if ( 321 storeMinStoreFileAge.isPresent() 322 && storeMinStoreFileAge.getAsLong() < tempMinStoreFileAge 323 ) { 324 tempMinStoreFileAge = storeMinStoreFileAge.getAsLong(); 325 } 326 327 long storeHFiles = store.getNumHFiles(); 328 numHFiles += storeHFiles; 329 tempNumReferenceFiles += store.getNumReferenceFiles(); 330 331 OptionalDouble storeAvgStoreFileAge = store.getAvgStoreFileAge(); 332 if (storeAvgStoreFileAge.isPresent()) { 333 avgAgeNumerator += (long) storeAvgStoreFileAge.getAsDouble() * storeHFiles; 334 } 335 if (mixedReadsOnStore == null) { 336 mixedReadsOnStore = new HashMap<String, Long>(); 337 } 338 Long tempVal = mixedReadsOnStore.get(store.getColumnFamilyName()); 339 if (tempVal == null) { 340 tempVal = 0L; 341 } else { 342 tempVal += store.getMixedRowReadsCount(); 343 } 344 mixedReadsOnStore.put(store.getColumnFamilyName(), tempVal); 345 if (readsOnlyFromMemstore == null) { 346 readsOnlyFromMemstore = new HashMap<String, Long>(); 347 } 348 tempVal = readsOnlyFromMemstore.get(store.getColumnFamilyName()); 349 if (tempVal == null) { 350 tempVal = 0L; 351 } else { 352 tempVal += store.getMemstoreOnlyRowReadsCount(); 353 } 354 readsOnlyFromMemstore.put(store.getColumnFamilyName(), tempVal); 355 } 356 } 357 MutableLong regionCachedAmount = new MutableLong(0); 358 region.getBlockCache().getRegionCachedInfo() 359 .ifPresent(regionCacheRatio -> regionCachedAmount 360 .addAndGet(regionCacheRatio.getOrDefault(region.getRegionInfo().getEncodedName(), 0L))); 361 if (tempStoreFileSize > 0) { 362 LOG.debug("Region {}, had cached {} bytes from a total of {}", 363 region.getRegionInfo().getEncodedName(), regionCachedAmount.getValue(), 364 tempStoreFileSize); 365 currentRegionCacheRatio = regionCachedAmount.floatValue() / tempStoreFileSize; 366 if (DataTieringManager.getInstance() != null) { 367 currentRegionColdDataRatio = DataTieringManager.getInstance().getRegionColdDataSize() 368 .getOrDefault(region.getRegionInfo().getEncodedName(), new Pair<>(null, 0L)) 369 .getSecond() / (float) tempStoreFileSize; 370 } 371 } 372 numStoreFiles = tempNumStoreFiles; 373 storeRefCount = tempStoreRefCount; 374 maxCompactedStoreFileRefCount = tempMaxCompactedStoreFileRefCount; 375 memstoreSize = tempMemstoreSize; 376 memstoreHeapSize = tempMemstoreHeapSize; 377 memstoreOffHeapSize = tempMemstoreOffHeapSize; 378 storeFileSize = tempStoreFileSize; 379 maxStoreFileAge = tempMaxStoreFileAge; 380 if (tempMinStoreFileAge != Long.MAX_VALUE) { 381 minStoreFileAge = tempMinStoreFileAge; 382 } 383 384 if (numHFiles != 0) { 385 avgStoreFileAge = avgAgeNumerator / numHFiles; 386 } 387 388 numReferenceFiles = tempNumReferenceFiles; 389 tempMaxCompactionQueueSize = getNumCompactionsQueued(); 390 tempMaxFlushQueueSize = getNumFlushesQueued(); 391 if (tempMaxCompactionQueueSize > maxCompactionQueueSize) { 392 maxCompactionQueueSize = tempMaxCompactionQueueSize; 393 } 394 if (tempMaxFlushQueueSize > maxFlushQueueSize) { 395 maxFlushQueueSize = tempMaxFlushQueueSize; 396 } 397 } catch (Throwable t) { 398 LOG.error("Failed to compute per-region metrics; will retry on next scheduled run", t); 399 } 400 } 401 } 402 403 @Override 404 public String getTableDescriptorHash() { 405 return tableDescriptorHash; 406 } 407 408 private String determineTableDescriptorHash() { 409 TableDescriptor tableDesc = this.region.getTableDescriptor(); 410 if (tableDesc == null) { 411 return UNKNOWN; 412 } 413 return tableDesc.getDescriptorHash(); 414 } 415 416 @Override 417 public void close() throws IOException { 418 regionMetricsUpdateTask.cancel(true); 419 } 420 421 /** 422 * Get the replica id of this region. 423 */ 424 @Override 425 public int getReplicaId() { 426 return region.getRegionInfo().getReplicaId(); 427 } 428 429}