1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.io.hfile;
20
21 import java.lang.ref.WeakReference;
22 import java.nio.ByteBuffer;
23 import java.util.EnumMap;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.PriorityQueue;
28 import java.util.SortedSet;
29 import java.util.TreeSet;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.concurrent.Executors;
32 import java.util.concurrent.ScheduledExecutorService;
33 import java.util.concurrent.TimeUnit;
34 import java.util.concurrent.atomic.AtomicLong;
35 import java.util.concurrent.locks.ReentrantLock;
36
37 import com.google.common.base.Objects;
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40 import org.apache.hadoop.hbase.classification.InterfaceAudience;
41 import org.apache.hadoop.conf.Configuration;
42 import org.apache.hadoop.hbase.io.HeapSize;
43 import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
44 import org.apache.hadoop.hbase.io.hfile.bucket.BucketCache;
45 import org.apache.hadoop.hbase.util.Bytes;
46 import org.apache.hadoop.hbase.util.ClassSize;
47 import org.apache.hadoop.hbase.util.HasThread;
48 import org.apache.hadoop.util.StringUtils;
49 import org.codehaus.jackson.annotate.JsonIgnoreProperties;
50
51 import com.google.common.annotations.VisibleForTesting;
52 import com.google.common.util.concurrent.ThreadFactoryBuilder;
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98 @InterfaceAudience.Private
99 @JsonIgnoreProperties({"encodingCountsForTest"})
100 public class LruBlockCache implements ResizableBlockCache, HeapSize {
101
102 static final Log LOG = LogFactory.getLog(LruBlockCache.class);
103
104
105
106
107
108 static final String LRU_MIN_FACTOR_CONFIG_NAME = "hbase.lru.blockcache.min.factor";
109
110
111
112
113 static final String LRU_ACCEPTABLE_FACTOR_CONFIG_NAME = "hbase.lru.blockcache.acceptable.factor";
114
115 static final String LRU_SINGLE_PERCENTAGE_CONFIG_NAME = "hbase.lru.blockcache.single.percentage";
116 static final String LRU_MULTI_PERCENTAGE_CONFIG_NAME = "hbase.lru.blockcache.multi.percentage";
117 static final String LRU_MEMORY_PERCENTAGE_CONFIG_NAME = "hbase.lru.blockcache.memory.percentage";
118
119
120
121
122
123
124 static final String LRU_IN_MEMORY_FORCE_MODE_CONFIG_NAME = "hbase.lru.rs.inmemoryforcemode";
125
126
127
128
129 static final float DEFAULT_LOAD_FACTOR = 0.75f;
130 static final int DEFAULT_CONCURRENCY_LEVEL = 16;
131
132
133 static final float DEFAULT_MIN_FACTOR = 0.95f;
134 static final float DEFAULT_ACCEPTABLE_FACTOR = 0.99f;
135
136
137 static final float DEFAULT_SINGLE_FACTOR = 0.25f;
138 static final float DEFAULT_MULTI_FACTOR = 0.50f;
139 static final float DEFAULT_MEMORY_FACTOR = 0.25f;
140
141 static final boolean DEFAULT_IN_MEMORY_FORCE_MODE = false;
142
143
144 static final int statThreadPeriod = 60 * 5;
145
146
147 private final Map<BlockCacheKey,LruCachedBlock> map;
148
149
150 private final ReentrantLock evictionLock = new ReentrantLock(true);
151
152
153 private volatile boolean evictionInProgress = false;
154
155
156 private final EvictionThread evictionThread;
157
158
159 private final ScheduledExecutorService scheduleThreadPool = Executors.newScheduledThreadPool(1,
160 new ThreadFactoryBuilder().setNameFormat("LruBlockCacheStatsExecutor").setDaemon(true).build());
161
162
163 private final AtomicLong size;
164
165
166 private final AtomicLong elements;
167
168
169 private final AtomicLong count;
170
171
172 private final CacheStats stats;
173
174
175 private long maxSize;
176
177
178 private long blockSize;
179
180
181 private float acceptableFactor;
182
183
184 private float minFactor;
185
186
187 private float singleFactor;
188
189
190 private float multiFactor;
191
192
193 private float memoryFactor;
194
195
196 private long overhead;
197
198
199 private boolean forceInMemory;
200
201
202 private BlockCache victimHandler = null;
203
204
205
206
207
208
209
210
211
212
213 public LruBlockCache(long maxSize, long blockSize) {
214 this(maxSize, blockSize, true);
215 }
216
217
218
219
220 public LruBlockCache(long maxSize, long blockSize, boolean evictionThread) {
221 this(maxSize, blockSize, evictionThread,
222 (int)Math.ceil(1.2*maxSize/blockSize),
223 DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL,
224 DEFAULT_MIN_FACTOR, DEFAULT_ACCEPTABLE_FACTOR,
225 DEFAULT_SINGLE_FACTOR,
226 DEFAULT_MULTI_FACTOR,
227 DEFAULT_MEMORY_FACTOR,
228 false
229 );
230 }
231
232 public LruBlockCache(long maxSize, long blockSize, boolean evictionThread, Configuration conf) {
233 this(maxSize, blockSize, evictionThread,
234 (int)Math.ceil(1.2*maxSize/blockSize),
235 DEFAULT_LOAD_FACTOR,
236 DEFAULT_CONCURRENCY_LEVEL,
237 conf.getFloat(LRU_MIN_FACTOR_CONFIG_NAME, DEFAULT_MIN_FACTOR),
238 conf.getFloat(LRU_ACCEPTABLE_FACTOR_CONFIG_NAME, DEFAULT_ACCEPTABLE_FACTOR),
239 conf.getFloat(LRU_SINGLE_PERCENTAGE_CONFIG_NAME, DEFAULT_SINGLE_FACTOR),
240 conf.getFloat(LRU_MULTI_PERCENTAGE_CONFIG_NAME, DEFAULT_MULTI_FACTOR),
241 conf.getFloat(LRU_MEMORY_PERCENTAGE_CONFIG_NAME, DEFAULT_MEMORY_FACTOR),
242 conf.getBoolean(LRU_IN_MEMORY_FORCE_MODE_CONFIG_NAME, DEFAULT_IN_MEMORY_FORCE_MODE)
243 );
244 }
245
246 public LruBlockCache(long maxSize, long blockSize, Configuration conf) {
247 this(maxSize, blockSize, true, conf);
248 }
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264 public LruBlockCache(long maxSize, long blockSize, boolean evictionThread,
265 int mapInitialSize, float mapLoadFactor, int mapConcurrencyLevel,
266 float minFactor, float acceptableFactor, float singleFactor,
267 float multiFactor, float memoryFactor, boolean forceInMemory) {
268 if(singleFactor + multiFactor + memoryFactor != 1 ||
269 singleFactor < 0 || multiFactor < 0 || memoryFactor < 0) {
270 throw new IllegalArgumentException("Single, multi, and memory factors " +
271 " should be non-negative and total 1.0");
272 }
273 if(minFactor >= acceptableFactor) {
274 throw new IllegalArgumentException("minFactor must be smaller than acceptableFactor");
275 }
276 if(minFactor >= 1.0f || acceptableFactor >= 1.0f) {
277 throw new IllegalArgumentException("all factors must be < 1");
278 }
279 this.maxSize = maxSize;
280 this.blockSize = blockSize;
281 this.forceInMemory = forceInMemory;
282 map = new ConcurrentHashMap<BlockCacheKey,LruCachedBlock>(mapInitialSize,
283 mapLoadFactor, mapConcurrencyLevel);
284 this.minFactor = minFactor;
285 this.acceptableFactor = acceptableFactor;
286 this.singleFactor = singleFactor;
287 this.multiFactor = multiFactor;
288 this.memoryFactor = memoryFactor;
289 this.stats = new CacheStats(this.getClass().getSimpleName());
290 this.count = new AtomicLong(0);
291 this.elements = new AtomicLong(0);
292 this.overhead = calculateOverhead(maxSize, blockSize, mapConcurrencyLevel);
293 this.size = new AtomicLong(this.overhead);
294 if(evictionThread) {
295 this.evictionThread = new EvictionThread(this);
296 this.evictionThread.start();
297 } else {
298 this.evictionThread = null;
299 }
300
301
302 this.scheduleThreadPool.scheduleAtFixedRate(new StatisticsThread(this),
303 statThreadPeriod, statThreadPeriod, TimeUnit.SECONDS);
304 }
305
306 @Override
307 public void setMaxSize(long maxSize) {
308 this.maxSize = maxSize;
309 if(this.size.get() > acceptableSize() && !evictionInProgress) {
310 runEviction();
311 }
312 }
313
314
315
316
317
318
319
320
321
322
323
324
325
326 @Override
327 public void cacheBlock(BlockCacheKey cacheKey, Cacheable buf, boolean inMemory,
328 final boolean cacheDataInL1) {
329 LruCachedBlock cb = map.get(cacheKey);
330 if (cb != null) {
331
332 if (compare(buf, cb.getBuffer()) != 0) {
333 throw new RuntimeException("Cached block contents differ, which should not have happened."
334 + "cacheKey:" + cacheKey);
335 }
336 String msg = "Cached an already cached block: " + cacheKey + " cb:" + cb.getCacheKey();
337 msg += ". This is harmless and can happen in rare cases (see HBASE-8547)";
338 LOG.warn(msg);
339 return;
340 }
341 cb = new LruCachedBlock(cacheKey, buf, count.incrementAndGet(), inMemory);
342 long newSize = updateSizeMetrics(cb, false);
343 map.put(cacheKey, cb);
344 long val = elements.incrementAndGet();
345 if (LOG.isTraceEnabled()) {
346 long size = map.size();
347 assertCounterSanity(size, val);
348 }
349 if (newSize > acceptableSize() && !evictionInProgress) {
350 runEviction();
351 }
352 }
353
354
355
356
357
358 private static void assertCounterSanity(long mapSize, long counterVal) {
359 if (counterVal < 0) {
360 LOG.trace("counterVal overflow. Assertions unreliable. counterVal=" + counterVal +
361 ", mapSize=" + mapSize);
362 return;
363 }
364 if (mapSize < Integer.MAX_VALUE) {
365 double pct_diff = Math.abs((((double) counterVal) / ((double) mapSize)) - 1.);
366 if (pct_diff > 0.05) {
367 LOG.trace("delta between reported and actual size > 5%. counterVal=" + counterVal +
368 ", mapSize=" + mapSize);
369 }
370 }
371 }
372
373 private int compare(Cacheable left, Cacheable right) {
374 ByteBuffer l = ByteBuffer.allocate(left.getSerializedLength());
375 left.serialize(l);
376 ByteBuffer r = ByteBuffer.allocate(right.getSerializedLength());
377 right.serialize(r);
378 return Bytes.compareTo(l.array(), l.arrayOffset(), l.limit(),
379 r.array(), r.arrayOffset(), r.limit());
380 }
381
382
383
384
385
386
387
388 public void cacheBlock(BlockCacheKey cacheKey, Cacheable buf) {
389 cacheBlock(cacheKey, buf, false, false);
390 }
391
392
393
394
395
396
397
398
399
400 protected long updateSizeMetrics(LruCachedBlock cb, boolean evict) {
401 long heapsize = cb.heapSize();
402 if (evict) {
403 heapsize *= -1;
404 }
405 return size.addAndGet(heapsize);
406 }
407
408
409
410
411
412
413
414
415
416
417 @Override
418 public Cacheable getBlock(BlockCacheKey cacheKey, boolean caching, boolean repeat,
419 boolean updateCacheMetrics) {
420 LruCachedBlock cb = map.get(cacheKey);
421 if (cb == null) {
422 if (!repeat && updateCacheMetrics) {
423 stats.miss(caching, cacheKey.isPrimary(), cacheKey.getBlockType());
424 }
425
426
427
428 if (victimHandler != null && !repeat) {
429 Cacheable result = victimHandler.getBlock(cacheKey, caching, repeat, updateCacheMetrics);
430
431
432 if (result != null && caching) {
433 cacheBlock(cacheKey, result,
434 }
435 return result;
436 }
437 return null;
438 }
439 if (updateCacheMetrics) stats.hit(caching, cacheKey.isPrimary(), cacheKey.getBlockType());
440 cb.access(count.incrementAndGet());
441 return cb.getBuffer();
442 }
443
444
445
446
447
448
449 public boolean containsBlock(BlockCacheKey cacheKey) {
450 return map.containsKey(cacheKey);
451 }
452
453 @Override
454 public boolean evictBlock(BlockCacheKey cacheKey) {
455 LruCachedBlock cb = map.get(cacheKey);
456 if (cb == null) return false;
457 evictBlock(cb, false);
458 return true;
459 }
460
461
462
463
464
465
466
467
468
469
470
471 @Override
472 public int evictBlocksByHfileName(String hfileName) {
473 int numEvicted = 0;
474 for (BlockCacheKey key : map.keySet()) {
475 if (key.getHfileName().equals(hfileName)) {
476 if (evictBlock(key))
477 ++numEvicted;
478 }
479 }
480 if (victimHandler != null) {
481 numEvicted += victimHandler.evictBlocksByHfileName(hfileName);
482 }
483 return numEvicted;
484 }
485
486
487
488
489
490
491
492
493
494 protected long evictBlock(LruCachedBlock block, boolean evictedByEvictionProcess) {
495 map.remove(block.getCacheKey());
496 updateSizeMetrics(block, true);
497 long val = elements.decrementAndGet();
498 if (LOG.isTraceEnabled()) {
499 long size = map.size();
500 assertCounterSanity(size, val);
501 }
502 stats.evicted(block.getCachedTime(), block.getCacheKey().isPrimary());
503 if (evictedByEvictionProcess && victimHandler != null) {
504 if (victimHandler instanceof BucketCache) {
505 boolean wait = getCurrentSize() < acceptableSize();
506 boolean inMemory = block.getPriority() == BlockPriority.MEMORY;
507 ((BucketCache)victimHandler).cacheBlockWithWait(block.getCacheKey(), block.getBuffer(),
508 inMemory, wait);
509 } else {
510 victimHandler.cacheBlock(block.getCacheKey(), block.getBuffer());
511 }
512 }
513 return block.heapSize();
514 }
515
516
517
518
519 private void runEviction() {
520 if(evictionThread == null) {
521 evict();
522 } else {
523 evictionThread.evict();
524 }
525 }
526
527
528
529
530 void evict() {
531
532
533 if(!evictionLock.tryLock()) return;
534
535 try {
536 evictionInProgress = true;
537 long currentSize = this.size.get();
538 long bytesToFree = currentSize - minSize();
539
540 if (LOG.isTraceEnabled()) {
541 LOG.trace("Block cache LRU eviction started; Attempting to free " +
542 StringUtils.byteDesc(bytesToFree) + " of total=" +
543 StringUtils.byteDesc(currentSize));
544 }
545
546 if(bytesToFree <= 0) return;
547
548
549 BlockBucket bucketSingle = new BlockBucket("single", bytesToFree, blockSize,
550 singleSize());
551 BlockBucket bucketMulti = new BlockBucket("multi", bytesToFree, blockSize,
552 multiSize());
553 BlockBucket bucketMemory = new BlockBucket("memory", bytesToFree, blockSize,
554 memorySize());
555
556
557 for(LruCachedBlock cachedBlock : map.values()) {
558 switch(cachedBlock.getPriority()) {
559 case SINGLE: {
560 bucketSingle.add(cachedBlock);
561 break;
562 }
563 case MULTI: {
564 bucketMulti.add(cachedBlock);
565 break;
566 }
567 case MEMORY: {
568 bucketMemory.add(cachedBlock);
569 break;
570 }
571 }
572 }
573
574 long bytesFreed = 0;
575 if (forceInMemory || memoryFactor > 0.999f) {
576 long s = bucketSingle.totalSize();
577 long m = bucketMulti.totalSize();
578 if (bytesToFree > (s + m)) {
579
580
581 bytesFreed = bucketSingle.free(s);
582 bytesFreed += bucketMulti.free(m);
583 if (LOG.isTraceEnabled()) {
584 LOG.trace("freed " + StringUtils.byteDesc(bytesFreed) +
585 " from single and multi buckets");
586 }
587 bytesFreed += bucketMemory.free(bytesToFree - bytesFreed);
588 if (LOG.isTraceEnabled()) {
589 LOG.trace("freed " + StringUtils.byteDesc(bytesFreed) +
590 " total from all three buckets ");
591 }
592 } else {
593
594
595
596 long bytesRemain = s + m - bytesToFree;
597 if (3 * s <= bytesRemain) {
598
599
600 bytesFreed = bucketMulti.free(bytesToFree);
601 } else if (3 * m <= 2 * bytesRemain) {
602
603
604 bytesFreed = bucketSingle.free(bytesToFree);
605 } else {
606
607 bytesFreed = bucketSingle.free(s - bytesRemain / 3);
608 if (bytesFreed < bytesToFree) {
609 bytesFreed += bucketMulti.free(bytesToFree - bytesFreed);
610 }
611 }
612 }
613 } else {
614 PriorityQueue<BlockBucket> bucketQueue =
615 new PriorityQueue<BlockBucket>(3);
616
617 bucketQueue.add(bucketSingle);
618 bucketQueue.add(bucketMulti);
619 bucketQueue.add(bucketMemory);
620
621 int remainingBuckets = 3;
622
623 BlockBucket bucket;
624 while((bucket = bucketQueue.poll()) != null) {
625 long overflow = bucket.overflow();
626 if(overflow > 0) {
627 long bucketBytesToFree = Math.min(overflow,
628 (bytesToFree - bytesFreed) / remainingBuckets);
629 bytesFreed += bucket.free(bucketBytesToFree);
630 }
631 remainingBuckets--;
632 }
633 }
634
635 if (LOG.isTraceEnabled()) {
636 long single = bucketSingle.totalSize();
637 long multi = bucketMulti.totalSize();
638 long memory = bucketMemory.totalSize();
639 LOG.trace("Block cache LRU eviction completed; " +
640 "freed=" + StringUtils.byteDesc(bytesFreed) + ", " +
641 "total=" + StringUtils.byteDesc(this.size.get()) + ", " +
642 "single=" + StringUtils.byteDesc(single) + ", " +
643 "multi=" + StringUtils.byteDesc(multi) + ", " +
644 "memory=" + StringUtils.byteDesc(memory));
645 }
646 } finally {
647 stats.evict();
648 evictionInProgress = false;
649 evictionLock.unlock();
650 }
651 }
652
653 @Override
654 public String toString() {
655 return Objects.toStringHelper(this)
656 .add("blockCount", getBlockCount())
657 .add("currentSize", getCurrentSize())
658 .add("freeSize", getFreeSize())
659 .add("maxSize", getMaxSize())
660 .add("heapSize", heapSize())
661 .add("minSize", minSize())
662 .add("minFactor", minFactor)
663 .add("multiSize", multiSize())
664 .add("multiFactor", multiFactor)
665 .add("singleSize", singleSize())
666 .add("singleFactor", singleFactor)
667 .toString();
668 }
669
670
671
672
673
674
675
676 private class BlockBucket implements Comparable<BlockBucket> {
677
678 private final String name;
679 private LruCachedBlockQueue queue;
680 private long totalSize = 0;
681 private long bucketSize;
682
683 public BlockBucket(String name, long bytesToFree, long blockSize, long bucketSize) {
684 this.name = name;
685 this.bucketSize = bucketSize;
686 queue = new LruCachedBlockQueue(bytesToFree, blockSize);
687 totalSize = 0;
688 }
689
690 public void add(LruCachedBlock block) {
691 totalSize += block.heapSize();
692 queue.add(block);
693 }
694
695 public long free(long toFree) {
696 if (LOG.isTraceEnabled()) {
697 LOG.trace("freeing " + StringUtils.byteDesc(toFree) + " from " + this);
698 }
699 LruCachedBlock cb;
700 long freedBytes = 0;
701 while ((cb = queue.pollLast()) != null) {
702 freedBytes += evictBlock(cb, true);
703 if (freedBytes >= toFree) {
704 return freedBytes;
705 }
706 }
707 if (LOG.isTraceEnabled()) {
708 LOG.trace("freed " + StringUtils.byteDesc(freedBytes) + " from " + this);
709 }
710 return freedBytes;
711 }
712
713 public long overflow() {
714 return totalSize - bucketSize;
715 }
716
717 public long totalSize() {
718 return totalSize;
719 }
720
721 public int compareTo(BlockBucket that) {
722 if(this.overflow() == that.overflow()) return 0;
723 return this.overflow() > that.overflow() ? 1 : -1;
724 }
725
726 @Override
727 public boolean equals(Object that) {
728 if (that == null || !(that instanceof BlockBucket)){
729 return false;
730 }
731 return compareTo((BlockBucket)that) == 0;
732 }
733
734 @Override
735 public int hashCode() {
736 return Objects.hashCode(name, bucketSize, queue, totalSize);
737 }
738
739 @Override
740 public String toString() {
741 return Objects.toStringHelper(this)
742 .add("name", name)
743 .add("totalSize", StringUtils.byteDesc(totalSize))
744 .add("bucketSize", StringUtils.byteDesc(bucketSize))
745 .toString();
746 }
747 }
748
749
750
751
752
753 public long getMaxSize() {
754 return this.maxSize;
755 }
756
757 @Override
758 public long getCurrentSize() {
759 return this.size.get();
760 }
761
762 @Override
763 public long getFreeSize() {
764 return getMaxSize() - getCurrentSize();
765 }
766
767 @Override
768 public long size() {
769 return getMaxSize();
770 }
771
772 @Override
773 public long getBlockCount() {
774 return this.elements.get();
775 }
776
777 EvictionThread getEvictionThread() {
778 return this.evictionThread;
779 }
780
781
782
783
784
785
786
787 static class EvictionThread extends HasThread {
788 private WeakReference<LruBlockCache> cache;
789 private volatile boolean go = true;
790
791 private boolean enteringRun = false;
792
793 public EvictionThread(LruBlockCache cache) {
794 super(Thread.currentThread().getName() + ".LruBlockCache.EvictionThread");
795 setDaemon(true);
796 this.cache = new WeakReference<LruBlockCache>(cache);
797 }
798
799 @Override
800 public void run() {
801 enteringRun = true;
802 while (this.go) {
803 synchronized(this) {
804 try {
805 this.wait(1000 * 10
806 } catch(InterruptedException e) {
807 LOG.warn("Interrupted eviction thread ", e);
808 Thread.currentThread().interrupt();
809 }
810 }
811 LruBlockCache cache = this.cache.get();
812 if (cache == null) break;
813 cache.evict();
814 }
815 }
816
817 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="NN_NAKED_NOTIFY",
818 justification="This is what we want")
819 public void evict() {
820 synchronized(this) {
821 this.notifyAll();
822 }
823 }
824
825 synchronized void shutdown() {
826 this.go = false;
827 this.notifyAll();
828 }
829
830
831
832
833 boolean isEnteringRun() {
834 return this.enteringRun;
835 }
836 }
837
838
839
840
841 static class StatisticsThread extends Thread {
842 private final LruBlockCache lru;
843
844 public StatisticsThread(LruBlockCache lru) {
845 super("LruBlockCacheStats");
846 setDaemon(true);
847 this.lru = lru;
848 }
849
850 @Override
851 public void run() {
852 lru.logStats();
853 }
854 }
855
856 public void logStats() {
857
858 long totalSize = heapSize();
859 long freeSize = maxSize - totalSize;
860 LruBlockCache.LOG.info("totalSize=" + StringUtils.byteDesc(totalSize) + ", " +
861 "freeSize=" + StringUtils.byteDesc(freeSize) + ", " +
862 "max=" + StringUtils.byteDesc(this.maxSize) + ", " +
863 "blockCount=" + getBlockCount() + ", " +
864 "accesses=" + stats.getRequestCount() + ", " +
865 "hits=" + stats.getHitCount() + ", " +
866 "hitRatio=" + (stats.getHitCount() == 0 ?
867 "0" : (StringUtils.formatPercent(stats.getHitRatio(), 2)+ ", ")) + ", " +
868 "cachingAccesses=" + stats.getRequestCachingCount() + ", " +
869 "cachingHits=" + stats.getHitCachingCount() + ", " +
870 "cachingHitsRatio=" + (stats.getHitCachingCount() == 0 ?
871 "0,": (StringUtils.formatPercent(stats.getHitCachingRatio(), 2) + ", ")) +
872 "evictions=" + stats.getEvictionCount() + ", " +
873 "evicted=" + stats.getEvictedCount() + ", " +
874 "evictedPerRun=" + stats.evictedPerEviction());
875 }
876
877
878
879
880
881
882
883 public CacheStats getStats() {
884 return this.stats;
885 }
886
887 public final static long CACHE_FIXED_OVERHEAD = ClassSize.align(
888 (3 * Bytes.SIZEOF_LONG) + (9 * ClassSize.REFERENCE) +
889 (5 * Bytes.SIZEOF_FLOAT) + Bytes.SIZEOF_BOOLEAN
890 + ClassSize.OBJECT);
891
892 @Override
893 public long heapSize() {
894 return getCurrentSize();
895 }
896
897 public static long calculateOverhead(long maxSize, long blockSize, int concurrency){
898
899 return CACHE_FIXED_OVERHEAD + ClassSize.CONCURRENT_HASHMAP +
900 ((long)Math.ceil(maxSize*1.2/blockSize)
901 * ClassSize.CONCURRENT_HASHMAP_ENTRY) +
902 ((long)concurrency * ClassSize.CONCURRENT_HASHMAP_SEGMENT);
903 }
904
905 @Override
906 public Iterator<CachedBlock> iterator() {
907 final Iterator<LruCachedBlock> iterator = map.values().iterator();
908
909 return new Iterator<CachedBlock>() {
910 private final long now = System.nanoTime();
911
912 @Override
913 public boolean hasNext() {
914 return iterator.hasNext();
915 }
916
917 @Override
918 public CachedBlock next() {
919 final LruCachedBlock b = iterator.next();
920 return new CachedBlock() {
921 @Override
922 public String toString() {
923 return BlockCacheUtil.toString(this, now);
924 }
925
926 @Override
927 public BlockPriority getBlockPriority() {
928 return b.getPriority();
929 }
930
931 @Override
932 public BlockType getBlockType() {
933 return b.getBuffer().getBlockType();
934 }
935
936 @Override
937 public long getOffset() {
938 return b.getCacheKey().getOffset();
939 }
940
941 @Override
942 public long getSize() {
943 return b.getBuffer().heapSize();
944 }
945
946 @Override
947 public long getCachedTime() {
948 return b.getCachedTime();
949 }
950
951 @Override
952 public String getFilename() {
953 return b.getCacheKey().getHfileName();
954 }
955
956 @Override
957 public int compareTo(CachedBlock other) {
958 int diff = this.getFilename().compareTo(other.getFilename());
959 if (diff != 0) return diff;
960 diff = (int)(this.getOffset() - other.getOffset());
961 if (diff != 0) return diff;
962 if (other.getCachedTime() < 0 || this.getCachedTime() < 0) {
963 throw new IllegalStateException("" + this.getCachedTime() + ", " +
964 other.getCachedTime());
965 }
966 return (int)(other.getCachedTime() - this.getCachedTime());
967 }
968
969 @Override
970 public int hashCode() {
971 return b.hashCode();
972 }
973
974 @Override
975 public boolean equals(Object obj) {
976 if (obj instanceof CachedBlock) {
977 CachedBlock cb = (CachedBlock)obj;
978 return compareTo(cb) == 0;
979 } else {
980 return false;
981 }
982 }
983 };
984 }
985
986 @Override
987 public void remove() {
988 throw new UnsupportedOperationException();
989 }
990 };
991 }
992
993
994
995 long acceptableSize() {
996 return (long)Math.floor(this.maxSize * this.acceptableFactor);
997 }
998 private long minSize() {
999 return (long)Math.floor(this.maxSize * this.minFactor);
1000 }
1001 private long singleSize() {
1002 return (long)Math.floor(this.maxSize * this.singleFactor * this.minFactor);
1003 }
1004 private long multiSize() {
1005 return (long)Math.floor(this.maxSize * this.multiFactor * this.minFactor);
1006 }
1007 private long memorySize() {
1008 return (long)Math.floor(this.maxSize * this.memoryFactor * this.minFactor);
1009 }
1010
1011 public void shutdown() {
1012 if (victimHandler != null)
1013 victimHandler.shutdown();
1014 this.scheduleThreadPool.shutdown();
1015 for (int i = 0; i < 10; i++) {
1016 if (!this.scheduleThreadPool.isShutdown()) {
1017 try {
1018 Thread.sleep(10);
1019 } catch (InterruptedException e) {
1020 LOG.warn("Interrupted while sleeping");
1021 Thread.currentThread().interrupt();
1022 break;
1023 }
1024 }
1025 }
1026
1027 if (!this.scheduleThreadPool.isShutdown()) {
1028 List<Runnable> runnables = this.scheduleThreadPool.shutdownNow();
1029 LOG.debug("Still running " + runnables);
1030 }
1031 this.evictionThread.shutdown();
1032 }
1033
1034
1035 @VisibleForTesting
1036 public void clearCache() {
1037 this.map.clear();
1038 this.elements.set(0);
1039 }
1040
1041
1042
1043
1044
1045 @VisibleForTesting
1046 SortedSet<String> getCachedFileNamesForTest() {
1047 SortedSet<String> fileNames = new TreeSet<String>();
1048 for (BlockCacheKey cacheKey : map.keySet()) {
1049 fileNames.add(cacheKey.getHfileName());
1050 }
1051 return fileNames;
1052 }
1053
1054 @VisibleForTesting
1055 Map<BlockType, Integer> getBlockTypeCountsForTest() {
1056 Map<BlockType, Integer> counts =
1057 new EnumMap<BlockType, Integer>(BlockType.class);
1058 for (LruCachedBlock cb : map.values()) {
1059 BlockType blockType = ((Cacheable)cb.getBuffer()).getBlockType();
1060 Integer count = counts.get(blockType);
1061 counts.put(blockType, (count == null ? 0 : count) + 1);
1062 }
1063 return counts;
1064 }
1065
1066 @VisibleForTesting
1067 public Map<DataBlockEncoding, Integer> getEncodingCountsForTest() {
1068 Map<DataBlockEncoding, Integer> counts =
1069 new EnumMap<DataBlockEncoding, Integer>(DataBlockEncoding.class);
1070 for (LruCachedBlock block : map.values()) {
1071 DataBlockEncoding encoding =
1072 ((HFileBlock) block.getBuffer()).getDataBlockEncoding();
1073 Integer count = counts.get(encoding);
1074 counts.put(encoding, (count == null ? 0 : count) + 1);
1075 }
1076 return counts;
1077 }
1078
1079 public void setVictimCache(BlockCache handler) {
1080 assert victimHandler == null;
1081 victimHandler = handler;
1082 }
1083
1084 @VisibleForTesting
1085 Map<BlockCacheKey, LruCachedBlock> getMapForTests() {
1086 return map;
1087 }
1088
1089 BlockCache getVictimHandler() {
1090 return this.victimHandler;
1091 }
1092
1093 @Override
1094 public BlockCache[] getBlockCaches() {
1095 return null;
1096 }
1097 }