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) stats.miss(caching);
423
424
425
426 if (victimHandler != null && !repeat) {
427 Cacheable result = victimHandler.getBlock(cacheKey, caching, repeat, updateCacheMetrics);
428
429
430 if (result != null && caching) {
431 cacheBlock(cacheKey, result,
432 }
433 return result;
434 }
435 return null;
436 }
437 if (updateCacheMetrics) stats.hit(caching);
438 cb.access(count.incrementAndGet());
439 return cb.getBuffer();
440 }
441
442
443
444
445
446
447 public boolean containsBlock(BlockCacheKey cacheKey) {
448 return map.containsKey(cacheKey);
449 }
450
451 @Override
452 public boolean evictBlock(BlockCacheKey cacheKey) {
453 LruCachedBlock cb = map.get(cacheKey);
454 if (cb == null) return false;
455 evictBlock(cb, false);
456 return true;
457 }
458
459
460
461
462
463
464
465
466
467
468
469 @Override
470 public int evictBlocksByHfileName(String hfileName) {
471 int numEvicted = 0;
472 for (BlockCacheKey key : map.keySet()) {
473 if (key.getHfileName().equals(hfileName)) {
474 if (evictBlock(key))
475 ++numEvicted;
476 }
477 }
478 if (victimHandler != null) {
479 numEvicted += victimHandler.evictBlocksByHfileName(hfileName);
480 }
481 return numEvicted;
482 }
483
484
485
486
487
488
489
490
491
492 protected long evictBlock(LruCachedBlock block, boolean evictedByEvictionProcess) {
493 map.remove(block.getCacheKey());
494 updateSizeMetrics(block, true);
495 long val = elements.decrementAndGet();
496 if (LOG.isTraceEnabled()) {
497 long size = map.size();
498 assertCounterSanity(size, val);
499 }
500 stats.evicted(block.getCachedTime());
501 if (evictedByEvictionProcess && victimHandler != null) {
502 if (victimHandler instanceof BucketCache) {
503 boolean wait = getCurrentSize() < acceptableSize();
504 boolean inMemory = block.getPriority() == BlockPriority.MEMORY;
505 ((BucketCache)victimHandler).cacheBlockWithWait(block.getCacheKey(), block.getBuffer(),
506 inMemory, wait);
507 } else {
508 victimHandler.cacheBlock(block.getCacheKey(), block.getBuffer());
509 }
510 }
511 return block.heapSize();
512 }
513
514
515
516
517 private void runEviction() {
518 if(evictionThread == null) {
519 evict();
520 } else {
521 evictionThread.evict();
522 }
523 }
524
525
526
527
528 void evict() {
529
530
531 if(!evictionLock.tryLock()) return;
532
533 try {
534 evictionInProgress = true;
535 long currentSize = this.size.get();
536 long bytesToFree = currentSize - minSize();
537
538 if (LOG.isTraceEnabled()) {
539 LOG.trace("Block cache LRU eviction started; Attempting to free " +
540 StringUtils.byteDesc(bytesToFree) + " of total=" +
541 StringUtils.byteDesc(currentSize));
542 }
543
544 if(bytesToFree <= 0) return;
545
546
547 BlockBucket bucketSingle = new BlockBucket("single", bytesToFree, blockSize,
548 singleSize());
549 BlockBucket bucketMulti = new BlockBucket("multi", bytesToFree, blockSize,
550 multiSize());
551 BlockBucket bucketMemory = new BlockBucket("memory", bytesToFree, blockSize,
552 memorySize());
553
554
555 for(LruCachedBlock cachedBlock : map.values()) {
556 switch(cachedBlock.getPriority()) {
557 case SINGLE: {
558 bucketSingle.add(cachedBlock);
559 break;
560 }
561 case MULTI: {
562 bucketMulti.add(cachedBlock);
563 break;
564 }
565 case MEMORY: {
566 bucketMemory.add(cachedBlock);
567 break;
568 }
569 }
570 }
571
572 long bytesFreed = 0;
573 if (forceInMemory || memoryFactor > 0.999f) {
574 long s = bucketSingle.totalSize();
575 long m = bucketMulti.totalSize();
576 if (bytesToFree > (s + m)) {
577
578
579 bytesFreed = bucketSingle.free(s);
580 bytesFreed += bucketMulti.free(m);
581 if (LOG.isTraceEnabled()) {
582 LOG.trace("freed " + StringUtils.byteDesc(bytesFreed) +
583 " from single and multi buckets");
584 }
585 bytesFreed += bucketMemory.free(bytesToFree - bytesFreed);
586 if (LOG.isTraceEnabled()) {
587 LOG.trace("freed " + StringUtils.byteDesc(bytesFreed) +
588 " total from all three buckets ");
589 }
590 } else {
591
592
593
594 long bytesRemain = s + m - bytesToFree;
595 if (3 * s <= bytesRemain) {
596
597
598 bytesFreed = bucketMulti.free(bytesToFree);
599 } else if (3 * m <= 2 * bytesRemain) {
600
601
602 bytesFreed = bucketSingle.free(bytesToFree);
603 } else {
604
605 bytesFreed = bucketSingle.free(s - bytesRemain / 3);
606 if (bytesFreed < bytesToFree) {
607 bytesFreed += bucketMulti.free(bytesToFree - bytesFreed);
608 }
609 }
610 }
611 } else {
612 PriorityQueue<BlockBucket> bucketQueue =
613 new PriorityQueue<BlockBucket>(3);
614
615 bucketQueue.add(bucketSingle);
616 bucketQueue.add(bucketMulti);
617 bucketQueue.add(bucketMemory);
618
619 int remainingBuckets = 3;
620
621 BlockBucket bucket;
622 while((bucket = bucketQueue.poll()) != null) {
623 long overflow = bucket.overflow();
624 if(overflow > 0) {
625 long bucketBytesToFree = Math.min(overflow,
626 (bytesToFree - bytesFreed) / remainingBuckets);
627 bytesFreed += bucket.free(bucketBytesToFree);
628 }
629 remainingBuckets--;
630 }
631 }
632
633 if (LOG.isTraceEnabled()) {
634 long single = bucketSingle.totalSize();
635 long multi = bucketMulti.totalSize();
636 long memory = bucketMemory.totalSize();
637 LOG.trace("Block cache LRU eviction completed; " +
638 "freed=" + StringUtils.byteDesc(bytesFreed) + ", " +
639 "total=" + StringUtils.byteDesc(this.size.get()) + ", " +
640 "single=" + StringUtils.byteDesc(single) + ", " +
641 "multi=" + StringUtils.byteDesc(multi) + ", " +
642 "memory=" + StringUtils.byteDesc(memory));
643 }
644 } finally {
645 stats.evict();
646 evictionInProgress = false;
647 evictionLock.unlock();
648 }
649 }
650
651 @Override
652 public String toString() {
653 return Objects.toStringHelper(this)
654 .add("blockCount", getBlockCount())
655 .add("currentSize", getCurrentSize())
656 .add("freeSize", getFreeSize())
657 .add("maxSize", getMaxSize())
658 .add("heapSize", heapSize())
659 .add("minSize", minSize())
660 .add("minFactor", minFactor)
661 .add("multiSize", multiSize())
662 .add("multiFactor", multiFactor)
663 .add("singleSize", singleSize())
664 .add("singleFactor", singleFactor)
665 .toString();
666 }
667
668
669
670
671
672
673
674 private class BlockBucket implements Comparable<BlockBucket> {
675
676 private final String name;
677 private LruCachedBlockQueue queue;
678 private long totalSize = 0;
679 private long bucketSize;
680
681 public BlockBucket(String name, long bytesToFree, long blockSize, long bucketSize) {
682 this.name = name;
683 this.bucketSize = bucketSize;
684 queue = new LruCachedBlockQueue(bytesToFree, blockSize);
685 totalSize = 0;
686 }
687
688 public void add(LruCachedBlock block) {
689 totalSize += block.heapSize();
690 queue.add(block);
691 }
692
693 public long free(long toFree) {
694 if (LOG.isTraceEnabled()) {
695 LOG.trace("freeing " + StringUtils.byteDesc(toFree) + " from " + this);
696 }
697 LruCachedBlock cb;
698 long freedBytes = 0;
699 while ((cb = queue.pollLast()) != null) {
700 freedBytes += evictBlock(cb, true);
701 if (freedBytes >= toFree) {
702 return freedBytes;
703 }
704 }
705 if (LOG.isTraceEnabled()) {
706 LOG.trace("freed " + StringUtils.byteDesc(freedBytes) + " from " + this);
707 }
708 return freedBytes;
709 }
710
711 public long overflow() {
712 return totalSize - bucketSize;
713 }
714
715 public long totalSize() {
716 return totalSize;
717 }
718
719 public int compareTo(BlockBucket that) {
720 if(this.overflow() == that.overflow()) return 0;
721 return this.overflow() > that.overflow() ? 1 : -1;
722 }
723
724 @Override
725 public boolean equals(Object that) {
726 if (that == null || !(that instanceof BlockBucket)){
727 return false;
728 }
729 return compareTo((BlockBucket)that) == 0;
730 }
731
732 @Override
733 public int hashCode() {
734 return Objects.hashCode(name, bucketSize, queue, totalSize);
735 }
736
737 @Override
738 public String toString() {
739 return Objects.toStringHelper(this)
740 .add("name", name)
741 .add("totalSize", StringUtils.byteDesc(totalSize))
742 .add("bucketSize", StringUtils.byteDesc(bucketSize))
743 .toString();
744 }
745 }
746
747
748
749
750
751 public long getMaxSize() {
752 return this.maxSize;
753 }
754
755 @Override
756 public long getCurrentSize() {
757 return this.size.get();
758 }
759
760 @Override
761 public long getFreeSize() {
762 return getMaxSize() - getCurrentSize();
763 }
764
765 @Override
766 public long size() {
767 return getMaxSize();
768 }
769
770 @Override
771 public long getBlockCount() {
772 return this.elements.get();
773 }
774
775 EvictionThread getEvictionThread() {
776 return this.evictionThread;
777 }
778
779
780
781
782
783
784
785 static class EvictionThread extends HasThread {
786 private WeakReference<LruBlockCache> cache;
787 private volatile boolean go = true;
788
789 private boolean enteringRun = false;
790
791 public EvictionThread(LruBlockCache cache) {
792 super(Thread.currentThread().getName() + ".LruBlockCache.EvictionThread");
793 setDaemon(true);
794 this.cache = new WeakReference<LruBlockCache>(cache);
795 }
796
797 @Override
798 public void run() {
799 enteringRun = true;
800 while (this.go) {
801 synchronized(this) {
802 try {
803 this.wait(1000 * 10
804 } catch(InterruptedException e) {
805 LOG.warn("Interrupted eviction thread ", e);
806 Thread.currentThread().interrupt();
807 }
808 }
809 LruBlockCache cache = this.cache.get();
810 if (cache == null) break;
811 cache.evict();
812 }
813 }
814
815 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="NN_NAKED_NOTIFY",
816 justification="This is what we want")
817 public void evict() {
818 synchronized(this) {
819 this.notifyAll();
820 }
821 }
822
823 synchronized void shutdown() {
824 this.go = false;
825 this.notifyAll();
826 }
827
828
829
830
831 boolean isEnteringRun() {
832 return this.enteringRun;
833 }
834 }
835
836
837
838
839 static class StatisticsThread extends Thread {
840 private final LruBlockCache lru;
841
842 public StatisticsThread(LruBlockCache lru) {
843 super("LruBlockCacheStats");
844 setDaemon(true);
845 this.lru = lru;
846 }
847
848 @Override
849 public void run() {
850 lru.logStats();
851 }
852 }
853
854 public void logStats() {
855
856 long totalSize = heapSize();
857 long freeSize = maxSize - totalSize;
858 LruBlockCache.LOG.info("totalSize=" + StringUtils.byteDesc(totalSize) + ", " +
859 "freeSize=" + StringUtils.byteDesc(freeSize) + ", " +
860 "max=" + StringUtils.byteDesc(this.maxSize) + ", " +
861 "blockCount=" + getBlockCount() + ", " +
862 "accesses=" + stats.getRequestCount() + ", " +
863 "hits=" + stats.getHitCount() + ", " +
864 "hitRatio=" + (stats.getHitCount() == 0 ?
865 "0" : (StringUtils.formatPercent(stats.getHitRatio(), 2)+ ", ")) + ", " +
866 "cachingAccesses=" + stats.getRequestCachingCount() + ", " +
867 "cachingHits=" + stats.getHitCachingCount() + ", " +
868 "cachingHitsRatio=" + (stats.getHitCachingCount() == 0 ?
869 "0,": (StringUtils.formatPercent(stats.getHitCachingRatio(), 2) + ", ")) +
870 "evictions=" + stats.getEvictionCount() + ", " +
871 "evicted=" + stats.getEvictedCount() + ", " +
872 "evictedPerRun=" + stats.evictedPerEviction());
873 }
874
875
876
877
878
879
880
881 public CacheStats getStats() {
882 return this.stats;
883 }
884
885 public final static long CACHE_FIXED_OVERHEAD = ClassSize.align(
886 (3 * Bytes.SIZEOF_LONG) + (9 * ClassSize.REFERENCE) +
887 (5 * Bytes.SIZEOF_FLOAT) + Bytes.SIZEOF_BOOLEAN
888 + ClassSize.OBJECT);
889
890 @Override
891 public long heapSize() {
892 return getCurrentSize();
893 }
894
895 public static long calculateOverhead(long maxSize, long blockSize, int concurrency){
896
897 return CACHE_FIXED_OVERHEAD + ClassSize.CONCURRENT_HASHMAP +
898 ((long)Math.ceil(maxSize*1.2/blockSize)
899 * ClassSize.CONCURRENT_HASHMAP_ENTRY) +
900 ((long)concurrency * ClassSize.CONCURRENT_HASHMAP_SEGMENT);
901 }
902
903 @Override
904 public Iterator<CachedBlock> iterator() {
905 final Iterator<LruCachedBlock> iterator = map.values().iterator();
906
907 return new Iterator<CachedBlock>() {
908 private final long now = System.nanoTime();
909
910 @Override
911 public boolean hasNext() {
912 return iterator.hasNext();
913 }
914
915 @Override
916 public CachedBlock next() {
917 final LruCachedBlock b = iterator.next();
918 return new CachedBlock() {
919 @Override
920 public String toString() {
921 return BlockCacheUtil.toString(this, now);
922 }
923
924 @Override
925 public BlockPriority getBlockPriority() {
926 return b.getPriority();
927 }
928
929 @Override
930 public BlockType getBlockType() {
931 return b.getBuffer().getBlockType();
932 }
933
934 @Override
935 public long getOffset() {
936 return b.getCacheKey().getOffset();
937 }
938
939 @Override
940 public long getSize() {
941 return b.getBuffer().heapSize();
942 }
943
944 @Override
945 public long getCachedTime() {
946 return b.getCachedTime();
947 }
948
949 @Override
950 public String getFilename() {
951 return b.getCacheKey().getHfileName();
952 }
953
954 @Override
955 public int compareTo(CachedBlock other) {
956 int diff = this.getFilename().compareTo(other.getFilename());
957 if (diff != 0) return diff;
958 diff = (int)(this.getOffset() - other.getOffset());
959 if (diff != 0) return diff;
960 if (other.getCachedTime() < 0 || this.getCachedTime() < 0) {
961 throw new IllegalStateException("" + this.getCachedTime() + ", " +
962 other.getCachedTime());
963 }
964 return (int)(other.getCachedTime() - this.getCachedTime());
965 }
966
967 @Override
968 public int hashCode() {
969 return b.hashCode();
970 }
971
972 @Override
973 public boolean equals(Object obj) {
974 if (obj instanceof CachedBlock) {
975 CachedBlock cb = (CachedBlock)obj;
976 return compareTo(cb) == 0;
977 } else {
978 return false;
979 }
980 }
981 };
982 }
983
984 @Override
985 public void remove() {
986 throw new UnsupportedOperationException();
987 }
988 };
989 }
990
991
992
993 long acceptableSize() {
994 return (long)Math.floor(this.maxSize * this.acceptableFactor);
995 }
996 private long minSize() {
997 return (long)Math.floor(this.maxSize * this.minFactor);
998 }
999 private long singleSize() {
1000 return (long)Math.floor(this.maxSize * this.singleFactor * this.minFactor);
1001 }
1002 private long multiSize() {
1003 return (long)Math.floor(this.maxSize * this.multiFactor * this.minFactor);
1004 }
1005 private long memorySize() {
1006 return (long)Math.floor(this.maxSize * this.memoryFactor * this.minFactor);
1007 }
1008
1009 public void shutdown() {
1010 if (victimHandler != null)
1011 victimHandler.shutdown();
1012 this.scheduleThreadPool.shutdown();
1013 for (int i = 0; i < 10; i++) {
1014 if (!this.scheduleThreadPool.isShutdown()) {
1015 try {
1016 Thread.sleep(10);
1017 } catch (InterruptedException e) {
1018 LOG.warn("Interrupted while sleeping");
1019 Thread.currentThread().interrupt();
1020 break;
1021 }
1022 }
1023 }
1024
1025 if (!this.scheduleThreadPool.isShutdown()) {
1026 List<Runnable> runnables = this.scheduleThreadPool.shutdownNow();
1027 LOG.debug("Still running " + runnables);
1028 }
1029 this.evictionThread.shutdown();
1030 }
1031
1032
1033 @VisibleForTesting
1034 public void clearCache() {
1035 this.map.clear();
1036 this.elements.set(0);
1037 }
1038
1039
1040
1041
1042
1043 @VisibleForTesting
1044 SortedSet<String> getCachedFileNamesForTest() {
1045 SortedSet<String> fileNames = new TreeSet<String>();
1046 for (BlockCacheKey cacheKey : map.keySet()) {
1047 fileNames.add(cacheKey.getHfileName());
1048 }
1049 return fileNames;
1050 }
1051
1052 @VisibleForTesting
1053 Map<BlockType, Integer> getBlockTypeCountsForTest() {
1054 Map<BlockType, Integer> counts =
1055 new EnumMap<BlockType, Integer>(BlockType.class);
1056 for (LruCachedBlock cb : map.values()) {
1057 BlockType blockType = ((Cacheable)cb.getBuffer()).getBlockType();
1058 Integer count = counts.get(blockType);
1059 counts.put(blockType, (count == null ? 0 : count) + 1);
1060 }
1061 return counts;
1062 }
1063
1064 @VisibleForTesting
1065 public Map<DataBlockEncoding, Integer> getEncodingCountsForTest() {
1066 Map<DataBlockEncoding, Integer> counts =
1067 new EnumMap<DataBlockEncoding, Integer>(DataBlockEncoding.class);
1068 for (LruCachedBlock block : map.values()) {
1069 DataBlockEncoding encoding =
1070 ((HFileBlock) block.getBuffer()).getDataBlockEncoding();
1071 Integer count = counts.get(encoding);
1072 counts.put(encoding, (count == null ? 0 : count) + 1);
1073 }
1074 return counts;
1075 }
1076
1077 public void setVictimCache(BlockCache handler) {
1078 assert victimHandler == null;
1079 victimHandler = handler;
1080 }
1081
1082 @VisibleForTesting
1083 Map<BlockCacheKey, LruCachedBlock> getMapForTests() {
1084 return map;
1085 }
1086
1087 BlockCache getVictimHandler() {
1088 return this.victimHandler;
1089 }
1090
1091 @Override
1092 public BlockCache[] getBlockCaches() {
1093 return null;
1094 }
1095 }