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.io.hfile;
019
020import java.util.Arrays;
021import java.util.Date;
022import java.util.concurrent.ScheduledExecutorService;
023import java.util.concurrent.ScheduledThreadPoolExecutor;
024import java.util.concurrent.TimeUnit;
025import java.util.concurrent.atomic.AtomicLong;
026import java.util.concurrent.atomic.LongAdder;
027import org.apache.hadoop.hbase.metrics.impl.FastLongHistogram;
028import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
029import org.apache.yetus.audience.InterfaceAudience;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033/**
034 * Class that implements cache metrics.
035 */
036@InterfaceAudience.Private
037public class CacheStats {
038
039  private static final Logger LOG = LoggerFactory.getLogger(CacheStats.class);
040
041  /**
042   * Sliding window statistics. The number of metric periods to include in sliding window hit ratio
043   * calculations.
044   */
045  static final int DEFAULT_WINDOW_PERIODS = 5;
046
047  /** The number of getBlock requests that were cache hits */
048  private final LongAdder hitCount = new LongAdder();
049
050  /** The number of getBlock requests that were cache hits from primary replica */
051  private final LongAdder primaryHitCount = new LongAdder();
052
053  /**
054   * The number of getBlock requests that were cache hits, but only from requests that were set to
055   * use the block cache. This is because all reads attempt to read from the block cache even if
056   * they will not put new blocks into the block cache. See HBASE-2253 for more information.
057   */
058  private final LongAdder hitCachingCount = new LongAdder();
059
060  /** The number of getBlock requests that were cache misses */
061  private final LongAdder missCount = new LongAdder();
062
063  /** The number of getBlock requests for primary replica that were cache misses */
064  private final LongAdder primaryMissCount = new LongAdder();
065  /**
066   * The number of getBlock requests that were cache misses, but only from requests that were set to
067   * use the block cache.
068   */
069  private final LongAdder missCachingCount = new LongAdder();
070
071  /** The number of times an eviction has occurred */
072  private final LongAdder evictionCount = new LongAdder();
073
074  /** The total number of blocks that have been evicted */
075  private final LongAdder evictedBlockCount = new LongAdder();
076
077  /** The total number of blocks for primary replica that have been evicted */
078  private final LongAdder primaryEvictedBlockCount = new LongAdder();
079
080  /** The total number of blocks that were not inserted. */
081  private final AtomicLong failedInserts = new AtomicLong(0);
082
083  /** Per Block Type Counts */
084  private final LongAdder dataMissCount = new LongAdder();
085  private final LongAdder leafIndexMissCount = new LongAdder();
086  private final LongAdder bloomChunkMissCount = new LongAdder();
087  private final LongAdder metaMissCount = new LongAdder();
088  private final LongAdder rootIndexMissCount = new LongAdder();
089  private final LongAdder intermediateIndexMissCount = new LongAdder();
090  private final LongAdder fileInfoMissCount = new LongAdder();
091  private final LongAdder generalBloomMetaMissCount = new LongAdder();
092  private final LongAdder deleteFamilyBloomMissCount = new LongAdder();
093  private final LongAdder trailerMissCount = new LongAdder();
094
095  private final LongAdder dataHitCount = new LongAdder();
096  private final LongAdder leafIndexHitCount = new LongAdder();
097  private final LongAdder bloomChunkHitCount = new LongAdder();
098  private final LongAdder metaHitCount = new LongAdder();
099  private final LongAdder rootIndexHitCount = new LongAdder();
100  private final LongAdder intermediateIndexHitCount = new LongAdder();
101  private final LongAdder fileInfoHitCount = new LongAdder();
102  private final LongAdder generalBloomMetaHitCount = new LongAdder();
103  private final LongAdder deleteFamilyBloomHitCount = new LongAdder();
104  private final LongAdder trailerHitCount = new LongAdder();
105
106  // Executor for periodic cache stats rolling
107  private ScheduledExecutorService metricsRollerScheduler;
108
109  /** The number of metrics periods to include in window */
110  private final int numPeriodsInWindow;
111  /** Hit counts for each period in window */
112  private final long[] hitCounts;
113  /** Caching hit counts for each period in window */
114  private final long[] hitCachingCounts;
115  /** Access counts for each period in window */
116  private final long[] requestCounts;
117  /** Caching access counts for each period in window */
118  private final long[] requestCachingCounts;
119  /** The initial date for each period in window */
120  private final Date[] windowPeriods;
121  /** Last hit count read */
122  private long lastHitCount = 0;
123  /** Last hit caching count read */
124  private long lastHitCachingCount = 0;
125  /** Last request count read */
126  private long lastRequestCount = 0;
127  /** Last request caching count read */
128  private long lastRequestCachingCount = 0;
129  /** Current window index (next to be updated) */
130  private int windowIndex = 0;
131  /**
132   * Keep running age at eviction time
133   */
134  private FastLongHistogram ageAtEviction;
135  private long startTime = System.nanoTime();
136
137  private int periodTimeInMinutes;
138
139  public CacheStats(final String name) {
140    this(name, DEFAULT_WINDOW_PERIODS, 0);
141  }
142
143  public CacheStats(final String name, int numPeriodsInWindow) {
144    this(name, numPeriodsInWindow, 0);
145  }
146
147  public CacheStats(final String name, int numPeriodsInWindow, int periodTimeInMinutes) {
148    this(name, numPeriodsInWindow, periodTimeInMinutes, TimeUnit.MINUTES);
149  }
150
151  CacheStats(final String name, int numPeriodsInWindow, int periodTime, TimeUnit unit) {
152    this.numPeriodsInWindow = numPeriodsInWindow;
153    this.hitCounts = new long[numPeriodsInWindow];
154    this.hitCachingCounts = new long[numPeriodsInWindow];
155    this.requestCounts = new long[numPeriodsInWindow];
156    this.requestCachingCounts = new long[numPeriodsInWindow];
157    this.windowPeriods = new Date[numPeriodsInWindow];
158    this.ageAtEviction = new FastLongHistogram();
159    this.periodTimeInMinutes = periodTime;
160    if (numPeriodsInWindow > 1 && periodTimeInMinutes > 0) {
161      this.metricsRollerScheduler = new ScheduledThreadPoolExecutor(1);
162      this.metricsRollerScheduler.scheduleAtFixedRate(() -> {
163        LOG.trace("Triggering metrics roll");
164        rollMetricsPeriod();
165        for (int i = 0; i < numPeriodsInWindow; i++) {
166          LOG.trace("period: {}, hit count: {}, request count: {}", i, hitCounts[i],
167            requestCounts[i]);
168        }
169      }, 1, periodTimeInMinutes, unit);
170    }
171  }
172
173  @Override
174  public String toString() {
175    AgeSnapshot snapshot = getAgeAtEvictionSnapshot();
176    return "hitCount=" + getHitCount() + ", hitCachingCount=" + getHitCachingCount()
177      + ", missCount=" + getMissCount() + ", missCachingCount=" + getMissCachingCount()
178      + ", evictionCount=" + getEvictionCount() + ", evictedBlockCount=" + getEvictedCount()
179      + ", primaryMissCount=" + getPrimaryMissCount() + ", primaryHitCount=" + getPrimaryHitCount()
180      + ", evictedAgeMean=" + snapshot.getMean();
181  }
182
183  public void miss(boolean caching, boolean primary, BlockType type) {
184    missCount.increment();
185    if (primary) primaryMissCount.increment();
186    if (caching) missCachingCount.increment();
187    if (type == null) {
188      return;
189    }
190    switch (type) {
191      case DATA:
192      case ENCODED_DATA:
193        dataMissCount.increment();
194        break;
195      case LEAF_INDEX:
196        leafIndexMissCount.increment();
197        break;
198      case BLOOM_CHUNK:
199        bloomChunkMissCount.increment();
200        break;
201      case META:
202        metaMissCount.increment();
203        break;
204      case INTERMEDIATE_INDEX:
205        intermediateIndexMissCount.increment();
206        break;
207      case ROOT_INDEX:
208        rootIndexMissCount.increment();
209        break;
210      case FILE_INFO:
211        fileInfoMissCount.increment();
212        break;
213      case GENERAL_BLOOM_META:
214        generalBloomMetaMissCount.increment();
215        break;
216      case DELETE_FAMILY_BLOOM_META:
217        deleteFamilyBloomMissCount.increment();
218        break;
219      case TRAILER:
220        trailerMissCount.increment();
221        break;
222      default:
223        // If there's a new type that's fine
224        // Ignore it for now. This is metrics don't exception.
225        break;
226    }
227  }
228
229  public void hit(boolean caching, boolean primary, BlockType type) {
230    hitCount.increment();
231    if (primary) primaryHitCount.increment();
232    if (caching) hitCachingCount.increment();
233
234    if (type == null) {
235      return;
236    }
237    switch (type) {
238      case DATA:
239      case ENCODED_DATA:
240        dataHitCount.increment();
241        break;
242      case LEAF_INDEX:
243        leafIndexHitCount.increment();
244        break;
245      case BLOOM_CHUNK:
246        bloomChunkHitCount.increment();
247        break;
248      case META:
249        metaHitCount.increment();
250        break;
251      case INTERMEDIATE_INDEX:
252        intermediateIndexHitCount.increment();
253        break;
254      case ROOT_INDEX:
255        rootIndexHitCount.increment();
256        break;
257      case FILE_INFO:
258        fileInfoHitCount.increment();
259        break;
260      case GENERAL_BLOOM_META:
261        generalBloomMetaHitCount.increment();
262        break;
263      case DELETE_FAMILY_BLOOM_META:
264        deleteFamilyBloomHitCount.increment();
265        break;
266      case TRAILER:
267        trailerHitCount.increment();
268        break;
269      default:
270        // If there's a new type that's fine
271        // Ignore it for now. This is metrics don't exception.
272        break;
273    }
274  }
275
276  public void evict() {
277    evictionCount.increment();
278  }
279
280  public void evicted(final long t, boolean primary) {
281    if (t > this.startTime) {
282      this.ageAtEviction.add((t - this.startTime) / BlockCacheUtil.NANOS_PER_SECOND, 1);
283    }
284    this.evictedBlockCount.increment();
285    if (primary) {
286      primaryEvictedBlockCount.increment();
287    }
288  }
289
290  public ScheduledExecutorService getMetricsRollerScheduler() {
291    return metricsRollerScheduler;
292  }
293
294  public long failInsert() {
295    return failedInserts.incrementAndGet();
296  }
297
298  // All of the counts of misses and hits.
299  public long getDataMissCount() {
300    return dataMissCount.sum();
301  }
302
303  public long getLeafIndexMissCount() {
304    return leafIndexMissCount.sum();
305  }
306
307  public long getBloomChunkMissCount() {
308    return bloomChunkMissCount.sum();
309  }
310
311  public long getMetaMissCount() {
312    return metaMissCount.sum();
313  }
314
315  public long getRootIndexMissCount() {
316    return rootIndexMissCount.sum();
317  }
318
319  public long getIntermediateIndexMissCount() {
320    return intermediateIndexMissCount.sum();
321  }
322
323  public long getFileInfoMissCount() {
324    return fileInfoMissCount.sum();
325  }
326
327  public long getGeneralBloomMetaMissCount() {
328    return generalBloomMetaMissCount.sum();
329  }
330
331  public long getDeleteFamilyBloomMissCount() {
332    return deleteFamilyBloomMissCount.sum();
333  }
334
335  public long getTrailerMissCount() {
336    return trailerMissCount.sum();
337  }
338
339  public long getDataHitCount() {
340    return dataHitCount.sum();
341  }
342
343  public long getLeafIndexHitCount() {
344    return leafIndexHitCount.sum();
345  }
346
347  public long getBloomChunkHitCount() {
348    return bloomChunkHitCount.sum();
349  }
350
351  public long getMetaHitCount() {
352    return metaHitCount.sum();
353  }
354
355  public long getRootIndexHitCount() {
356    return rootIndexHitCount.sum();
357  }
358
359  public long getIntermediateIndexHitCount() {
360    return intermediateIndexHitCount.sum();
361  }
362
363  public long getFileInfoHitCount() {
364    return fileInfoHitCount.sum();
365  }
366
367  public long getGeneralBloomMetaHitCount() {
368    return generalBloomMetaHitCount.sum();
369  }
370
371  public long getDeleteFamilyBloomHitCount() {
372    return deleteFamilyBloomHitCount.sum();
373  }
374
375  public long getTrailerHitCount() {
376    return trailerHitCount.sum();
377  }
378
379  public long getRequestCount() {
380    return getHitCount() + getMissCount();
381  }
382
383  public long getRequestCachingCount() {
384    return getHitCachingCount() + getMissCachingCount();
385  }
386
387  public long getMissCount() {
388    return missCount.sum();
389  }
390
391  public long getPrimaryMissCount() {
392    return primaryMissCount.sum();
393  }
394
395  public long getMissCachingCount() {
396    return missCachingCount.sum();
397  }
398
399  public long getHitCount() {
400    return hitCount.sum();
401  }
402
403  public long getPrimaryHitCount() {
404    return primaryHitCount.sum();
405  }
406
407  public long getHitCachingCount() {
408    return hitCachingCount.sum();
409  }
410
411  public long getEvictionCount() {
412    return evictionCount.sum();
413  }
414
415  public long getEvictedCount() {
416    return this.evictedBlockCount.sum();
417  }
418
419  public long getPrimaryEvictedCount() {
420    return primaryEvictedBlockCount.sum();
421  }
422
423  public double getHitRatio() {
424    double requestCount = getRequestCount();
425
426    if (requestCount == 0) {
427      return 0;
428    }
429
430    return getHitCount() / requestCount;
431  }
432
433  public double getHitCachingRatio() {
434    double requestCachingCount = getRequestCachingCount();
435
436    if (requestCachingCount == 0) {
437      return 0;
438    }
439
440    return getHitCachingCount() / requestCachingCount;
441  }
442
443  public double getMissRatio() {
444    double requestCount = getRequestCount();
445
446    if (requestCount == 0) {
447      return 0;
448    }
449
450    return getMissCount() / requestCount;
451  }
452
453  public double getMissCachingRatio() {
454    double requestCachingCount = getRequestCachingCount();
455
456    if (requestCachingCount == 0) {
457      return 0;
458    }
459
460    return getMissCachingCount() / requestCachingCount;
461  }
462
463  public double evictedPerEviction() {
464    double evictionCount = getEvictionCount();
465
466    if (evictionCount == 0) {
467      return 0;
468    }
469
470    return getEvictedCount() / evictionCount;
471  }
472
473  public long getFailedInserts() {
474    return failedInserts.get();
475  }
476
477  public void rollMetricsPeriod() {
478    windowPeriods[windowIndex] =
479      new Date((EnvironmentEdgeManager.currentTime() - (periodTimeInMinutes * 60 * 1000L)));
480    hitCounts[windowIndex] = getHitCount() - lastHitCount;
481    lastHitCount = getHitCount();
482    hitCachingCounts[windowIndex] = getHitCachingCount() - lastHitCachingCount;
483    lastHitCachingCount = getHitCachingCount();
484    requestCounts[windowIndex] = getRequestCount() - lastRequestCount;
485    lastRequestCount = getRequestCount();
486    requestCachingCounts[windowIndex] = getRequestCachingCount() - lastRequestCachingCount;
487    lastRequestCachingCount = getRequestCachingCount();
488    windowIndex = (windowIndex + 1) % numPeriodsInWindow;
489  }
490
491  public long[] getHitCounts() {
492    return hitCounts;
493  }
494
495  public long[] getRequestCounts() {
496    return requestCounts;
497  }
498
499  public Date[] getWindowPeriods() {
500    return windowPeriods;
501  }
502
503  public int getWindowIndex() {
504    return windowIndex;
505  }
506
507  public int getNumPeriodsInWindow() {
508    return numPeriodsInWindow;
509  }
510
511  public int getPeriodTimeInMinutes() {
512    return periodTimeInMinutes;
513  }
514
515  public long getSumHitCountsPastNPeriods() {
516    return sum(hitCounts);
517  }
518
519  public long getSumRequestCountsPastNPeriods() {
520    return sum(requestCounts);
521  }
522
523  public long getSumHitCachingCountsPastNPeriods() {
524    return sum(hitCachingCounts);
525  }
526
527  public long getSumRequestCachingCountsPastNPeriods() {
528    return sum(requestCachingCounts);
529  }
530
531  public double getHitRatioPastNPeriods() {
532    double ratio =
533      ((double) getSumHitCountsPastNPeriods() / (double) getSumRequestCountsPastNPeriods());
534    return Double.isNaN(ratio) ? 0 : ratio;
535  }
536
537  public double getHitCachingRatioPastNPeriods() {
538    double ratio = ((double) getSumHitCachingCountsPastNPeriods()
539      / (double) getSumRequestCachingCountsPastNPeriods());
540    return Double.isNaN(ratio) ? 0 : ratio;
541  }
542
543  public AgeSnapshot getAgeAtEvictionSnapshot() {
544    return new AgeSnapshot(this.ageAtEviction);
545  }
546
547  private static long sum(long[] counts) {
548    return Arrays.stream(counts).sum();
549  }
550}